- MVVM 模式基礎
- 視圖模型(ViewModel):
MainViewModel
類作為視圖模型,封裝了與視圖相關的屬性和命令。它實現了INotifyPropertyChanged
接口,當屬性值發生改變時,通過OnPropertyChanged
方法通知視圖進行更新,確保視圖與數據的一致性。 - 視圖(View):在 XAML 文件中定義,通過數據綁定將
Button
的Width
和Height
屬性與視圖模型的ButtonWidth
和ButtonHeight
屬性關聯起來,實現視圖對數據的展示。同時,將按鈕的點擊事件綁定到視圖模型的ResizeButtonCommand
命令,使視圖的交互操作能觸發視圖模型中的邏輯。
- 視圖模型(ViewModel):
- 動畫創建與管理
- Storyboard 初始化:在視圖模型的
InitializeStoryboard
方法中,創建了一個Storyboard
對象_resizeStoryboard
,用于管理和協調多個動畫。 - DoubleAnimation 創建:為按鈕的寬度和高度變化分別創建了
DoubleAnimation
對象。這些動畫定義了從初始值(From
屬性)到目標值(To
屬性)的過渡,這里目標值是初始值的 1.5 倍。動畫的持續時間由Duration
屬性設置為 0.5 秒,AutoReverse
屬性設置為true
,使動畫在到達目標值后自動反向播放,RepeatBehavior
屬性設置為RepeatBehavior.Forever
,讓動畫無限循環。
- Storyboard 初始化:在視圖模型的
- 命令綁定與參數傳遞
- MyCommand 實現:定義了
MyCommand
類來實現ICommand
接口,該類允許將一個Action<object>
作為參數傳遞給構造函數,在按鈕點擊時執行相應的邏輯。 - 命令綁定:在視圖模型的構造函數中,將
ResizeButtonCommand
初始化為MyCommand
的實例,并關聯到ExecuteResizeButtonCommand
方法,該方法處理按鈕點擊后的邏輯。 - 參數傳遞:在視圖中,通過
CommandParameter="{Binding ElementName=PART_Button}"
將按鈕自身作為參數傳遞給命令。在視圖模型的ExecuteResizeButtonCommand
方法中,通過判斷參數類型來獲取按鈕實例,以便正確設置動畫目標和啟動動畫。
- MyCommand 實現:定義了
- 事件處理與狀態管理
- 點擊事件處理:
ExecuteResizeButtonCommand
方法負責處理按鈕的點擊事件。它根據_isAnimating
標志判斷當前動畫狀態,若正在動畫,則停止動畫并將按鈕大小恢復到初始值;若未動畫,則啟動動畫。每次點擊按鈕時,都會切換_isAnimating
標志的值,以記錄動畫狀態。
- 點擊事件處理:
? ? ? 5.代碼
MainWindow.xaml
<Window x:Class="WpfAppButtonResize.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:local="clr-namespace:WpfAppButtonResize"xmlns:local2="clr-namespace:WpfAppButtonResize.ViewModel"mc:Ignorable="d"Title="MainWindow" Height="350" Width="525"><Window.DataContext><local2:MainWindowViewModel/></Window.DataContext><Grid><Button Content="點擊我" Width="{Binding ButtonWidth}" Height="{Binding ButtonHeight}" Command="{Binding ResizeButtonCommand}"CommandParameter="{Binding ElementName=PART_Button}"Name="PART_Button"/></Grid>
</Window>
MainWindowViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;namespace WpfAppButtonResize.ViewModel
{public class MainWindowViewModel:Notify{private double _buttonWidth = 100;private double _buttonHeight = 50;private bool _isAnimating = false;private Storyboard _resizeStoryboard;public double ButtonWidth{get { return _buttonWidth; }set{_buttonWidth = value;OnPropertyChanged();}}public double ButtonHeight{get { return _buttonHeight; }set{_buttonHeight = value;OnPropertyChanged();}}public ICommand ResizeButtonCommand { get; private set; }public MainWindowViewModel(){ResizeButtonCommand = new MyCommand(ExecuteResizeButtonCommand);InitializeStoryboard();}private void InitializeStoryboard(){_resizeStoryboard = new Storyboard();// 寬度動畫DoubleAnimation widthAnimation = new DoubleAnimation{From = _buttonWidth,To = _buttonWidth * 1.5,Duration = TimeSpan.FromSeconds(0.5),AutoReverse = true,RepeatBehavior = RepeatBehavior.Forever};// 這里后續在設置目標時會修正_resizeStoryboard.Children.Add(widthAnimation);// 高度動畫DoubleAnimation heightAnimation = new DoubleAnimation{From = _buttonHeight,To = _buttonHeight * 1.5,Duration = TimeSpan.FromSeconds(0.5),AutoReverse = true,RepeatBehavior = RepeatBehavior.Forever};_resizeStoryboard.Children.Add(heightAnimation);}private void ExecuteResizeButtonCommand(object parameter){if (parameter is Button button){if (_isAnimating){_resizeStoryboard.Stop();ButtonWidth = 100;ButtonHeight = 50;}else{// 設置寬度動畫的目標和屬性路徑Storyboard.SetTarget(_resizeStoryboard.Children[0] as DoubleAnimation, button);Storyboard.SetTargetProperty(_resizeStoryboard.Children[0] as DoubleAnimation, new PropertyPath("(FrameworkElement.Width)"));// 設置高度動畫的目標和屬性路徑Storyboard.SetTarget(_resizeStoryboard.Children[1] as DoubleAnimation, button);Storyboard.SetTargetProperty(_resizeStoryboard.Children[1] as DoubleAnimation, new PropertyPath("(FrameworkElement.Height)"));_resizeStoryboard.Begin();}_isAnimating = !_isAnimating;}}}
}
MyCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;namespace WpfAppButtonResize
{public class MyCommand : ICommand{private readonly Action _execute;private readonly Action<object> _execute2;private readonly Func<bool> _canExecute;public MyCommand(Action<object> execute){_execute2 = execute;}public MyCommand(Action execute, Func<bool> canExecute = null){_execute = execute;_canExecute = canExecute;}public event EventHandler CanExecuteChanged{add { CommandManager.RequerySuggested += value; }remove { CommandManager.RequerySuggested -= value; }}public bool CanExecute(object parameter){return _canExecute == null || _canExecute();}public void Execute(object parameter){if (_execute != null){_execute();}else if (_execute2 != null){_execute2(parameter);}}}
}
Notify.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;namespace WpfAppButtonResize
{public abstract class Notify : INotifyPropertyChanged{public event PropertyChangedEventHandler PropertyChanged;public void OnPropertyChanged([CallerMemberName] string name = null){PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));}}
}