WPF-实现按钮的动态变化

发布于:2025-03-23 ⋅ 阅读:(26) ⋅ 点赞:(0)
  1. MVVM 模式基础
    • 视图模型(ViewModel)MainViewModel类作为视图模型,封装了与视图相关的属性和命令。它实现了INotifyPropertyChanged接口,当属性值发生改变时,通过OnPropertyChanged方法通知视图进行更新,确保视图与数据的一致性。
    • 视图(View):在 XAML 文件中定义,通过数据绑定将ButtonWidthHeight属性与视图模型的ButtonWidthButtonHeight属性关联起来,实现视图对数据的展示。同时,将按钮的点击事件绑定到视图模型的ResizeButtonCommand命令,使视图的交互操作能触发视图模型中的逻辑。
  2. 动画创建与管理
    • Storyboard 初始化:在视图模型的InitializeStoryboard方法中,创建了一个Storyboard对象_resizeStoryboard,用于管理和协调多个动画。
    • DoubleAnimation 创建:为按钮的宽度和高度变化分别创建了DoubleAnimation对象。这些动画定义了从初始值(From属性)到目标值(To属性)的过渡,这里目标值是初始值的 1.5 倍。动画的持续时间由Duration属性设置为 0.5 秒,AutoReverse属性设置为true,使动画在到达目标值后自动反向播放,RepeatBehavior属性设置为RepeatBehavior.Forever,让动画无限循环。
  3. 命令绑定与参数传递
    • MyCommand 实现:定义了MyCommand类来实现ICommand接口,该类允许将一个Action<object>作为参数传递给构造函数,在按钮点击时执行相应的逻辑。
    • 命令绑定:在视图模型的构造函数中,将ResizeButtonCommand初始化为MyCommand的实例,并关联到ExecuteResizeButtonCommand方法,该方法处理按钮点击后的逻辑。
    • 参数传递:在视图中,通过CommandParameter="{Binding ElementName=PART_Button}"将按钮自身作为参数传递给命令。在视图模型的ExecuteResizeButtonCommand方法中,通过判断参数类型来获取按钮实例,以便正确设置动画目标和启动动画。
  4. 事件处理与状态管理
    • 点击事件处理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));
        }

    }
}