- 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));}}
}