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