WPF-实现按钮的动态变化

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

    }
}
相关推荐
Scout-leaf4 天前
WPF新手村教程(三)—— 路由事件
c#·wpf
柒.梧.6 天前
基于SpringBoot+JWT 实现Token登录认证与登录人信息查询
wpf
十月南城9 天前
Flink实时计算心智模型——流、窗口、水位线、状态与Checkpoint的协作
大数据·flink·wpf
听麟12 天前
HarmonyOS 6.0+ 跨端会议助手APP开发实战:多设备接续与智能纪要全流程落地
分布式·深度学习·华为·区块链·wpf·harmonyos
@hdd12 天前
Kubernetes 可观测性:Prometheus 监控、日志采集与告警
云原生·kubernetes·wpf·prometheus
zls36536512 天前
C# WPF canvas中绘制缺陷分布map
开发语言·c#·wpf
专注VB编程开发20年12 天前
c#Redis扣款锁的设计,多用户,多台电脑操作
wpf
闲人编程13 天前
定时任务与周期性调度
分布式·python·wpf·调度·cron·定时人物·周期性
zls36536513 天前
C# WPF canvas中绘制缺陷分布map并实现缩放
开发语言·c#·wpf
数据知道14 天前
PostgreSQL:Citus 分布式拓展,水平分片,支持海量数据与高并发
分布式·postgresql·wpf