• WPF使用MVVM(二)命令绑定


    WPF使用MVVM(二)-命令绑定

    上一节已经介绍了WPF的属性绑定,这使得我们只需要指定界面的DataContext,然后就可以让界面绑定我们的属性数据呢。

    但是上一节还遗留了一个问题就是我们的按钮的Click方法,依然是写在界面的后台中的,现在我们希望将按钮的Click方法也采用绑定的形式。

    原先是这样的:

        <Button
            Grid.Row="3"
            Grid.ColumnSpan="2"
            Margin="20"
            Click="Button_Click"
            Content="更新一下信息"
            FontSize="30"
            FontWeight="Bold" />
    

    希望变成这样:

            <Button
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="20"
                Click="{Binding ClickAction}"
                Content="更新一下信息"
                FontSize="30"
                FontWeight="Bold" />
    

    让我们的MainWindowVM(ViewModel)也提供一个方法,让我们绑定一下,这样界面的数据和按钮的点击处理逻辑,都放到了桥梁ViewModel中了,界面看起来也很清爽。

    Command

    WPF呢,为了让我们用MVVM的形式替换按钮的点击行为,给我们提供了一个Command的属性,让我们也可以像绑定属性的方式,来绑定我们的点击方法,具体的写法如下:

            <Button
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="20"
                Command="{Binding ClickAction}"
                Content="更新一下信息"
                FontSize="30"
                FontWeight="Bold" />
    

    之前写的Button_Click方法也可以直接删除了。

    注意:Command属性仅仅作为Click行为的绑定,其他行为,如鼠标移入、移出。。。等行为,要使用另外的MVVM方式进行绑定。(本文只介绍点击行为,后续介绍其他行为的MVVM实现)

    添加ClickAction的实现

    上面我们也删除了Button_Click方法,并且还给Button按钮的Command属性绑定了一个方法叫做ClickAction,接下来我们就要在MainWindowVM(ViewModel)中去添加这个方法。

    要实现绑定的方法ClickAction,就需要用到ICommand接口,需要我们自己创建类型去实现接口的CanExecuteExecuteCanExecuteChanged,下面直接贴一下实现接口的代码,需要新建一个类,名字我们取RelayCommand:

        public class RelayCommand : ICommand
        {
            /// <summary>
            /// 命令能否执行
            /// </summary>
            readonly Func<bool> _canExecute;
            /// <summary>
            /// 命令执行的方法
            /// </summary>
            readonly Action _execute;
    
            /// <summary>
            /// 命令的构造函数
            /// </summary>
            /// <param name="action">命令需执行的方法</param>
            /// <param name="canExecute">命令是否可以执行的方法</param>
            public RelayCommand(Action action, Func<bool> canExecute)
            {
                _execute = action;
                _canExecute = canExecute;
            }
    
            /// <summary>
            /// 判断命令是否可以执行
            /// </summary>
            /// <param name="parameter"></param>
            /// <returns></returns>
            public bool CanExecute(Object parameter)
            {
                if (_canExecute == null)
                    return true;
                return _canExecute();
            }
    
            /// <summary>
            /// 执行命令
            /// </summary>
            /// <param name="parameter"></param>
            public void Execute(Object parameter)
            {
                _execute();
            }
    
            /// <summary>
            /// 事件追加、移除
            /// </summary>
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    if (_canExecute != null)
                        CommandManager.RequerySuggested += value;
                }
                remove
                {
                    if (_canExecute != null)
                        CommandManager.RequerySuggested -= value;
                }
            }
    
        }
    

    创建这个类,就是为了在使用命令的时候, 创建一条命令出来用于绑定,这个类型接收两个参数,一个是命令执行的方法,另一个是有返回值的方法, 这个返回值bool用来确定,该条命令是否可以执行,如果命令不能被执行,则按钮的IsEnabled就被会设置成不可点击,下面我们来挨个看下效果

    MainWindowVM中创建一个命令

    刚才我们已经做好了创建命令的准备工作,下面直接创建一个命令,并给这个命令指定一个方法即可。

    MainWindowVM添加如下代码:

            /// <summary>
            /// 命令要执行的方法
            /// </summary>
            void UpdateNameExecute()
            {
                EmployeeM.Name = "王明(原属性修改)";
                EmployeeM = EmployeeM;
            }
    
            /// <summary>
            /// 命令是否可以执行
            /// </summary>
            /// <returns></returns>
            bool CanUpdateNameExecute()
            {
                return true;
            }
    
            /// <summary>
            /// 创建新命令
            /// </summary>
            public ICommand ClickAction
            {
                get
                {
                    return new RelayCommand(UpdateNameExecute, CanUpdateNameExecute);
                }
            }
    

    注意,创建这个新的命令的名字需要和我们界面按钮Command中绑定的名字一致,叫ClickAction

    这时候我们运行一下程序,点击按钮,可以看到命令是可以生效的。

    此时我们做一个小小的改动,我们将是否可以执行的方法返回为False

            /// <summary>
            /// 命令是否可以执行
            /// </summary>
            /// <returns></returns>
            bool CanUpdateNameExecute()
            {
                return false;
            }
    

    再次运行能够看到,界面中按钮已经是不可点击的状态了!

    image-20220117151414246

    所以我们绑定的这个命令是否可以执行,是直接影响到按钮能否被点击的!这个值会直接作用在按钮的IsEnabled上。

    命令带点私货-参数

    上面的命令就是纯命令,啥参数都没带上,有时候希望执行命令的时候,希望能够传个参数,那就需要改造一下了!

    采用泛型的形式,给Action加点料,重新贴一下RelayCommand的代码:

        public class RelayCommand<T> : ICommand
        {
            /// <summary>
            /// 命令能否执行
            /// </summary>
            readonly Func<bool> _canExecute;
            /// <summary>
            /// 命令执行的方法
            /// </summary>
            readonly Action<T> _execute;
    
            /// <summary>
            /// 命令的构造函数
            /// </summary>
            /// <param name="action">命令需执行的方法</param>
            /// <param name="canExecute">命令是否可以执行的方法</param>
            public RelayCommand(Action<T> action, Func<bool> canExecute)
            {
                _execute = action;
                _canExecute = canExecute;
            }
    
            /// <summary>
            /// 判断命令是否可以执行
            /// </summary>
            /// <param name="parameter"></param>
            /// <returns></returns>
            public bool CanExecute(Object parameter)
            {
                if (_canExecute == null)
                    return true;
                return _canExecute();
            }
    
            /// <summary>
            /// 执行命令
            /// </summary>
            /// <param name="parameter"></param>
            public void Execute(Object parameter)
            {
                _execute((T)parameter);
            }
    
            /// <summary>
            /// 事件追加、移除
            /// </summary>
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    if (_canExecute != null)
                        CommandManager.RequerySuggested += value;
                }
                remove
                {
                    if (_canExecute != null)
                        CommandManager.RequerySuggested -= value;
                }
            }
    
        }
    

    MainWindowVM(ViewModel)中创建的命令和给到的方法也要有点小变化:

            /// <summary>
            /// 命令要执行的方法
            /// </summary>
            void UpdateNameExecute(object sender)
            {
                EmployeeM.Name = "王明(原属性修改)";
                EmployeeM = EmployeeM;
            }
    
            /// <summary>
            /// 命令是否可以执行
            /// </summary>
            /// <returns></returns>
            bool CanUpdateNameExecute()
            {
                return true;
            }
            /// <summary>
            /// 创建新命令
            /// </summary>
            public ICommand ClickAction
            {
                get
                {
                    return new RelayCommand<object>(UpdateNameExecute, CanUpdateNameExecute);
                }
            }
    

    参数从哪里传呢, 当然是我们的界面传了,通过按钮的CommandParameter属性来传,这里我们将按钮自己传过去!

            <Button
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="20"
                Command="{Binding ClickAction}"
                CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}}"
                Content="更新一下信息"
                FontSize="30"
                FontWeight="Bold" />
    

    运行断点看一下,能够看到按钮自身已经当作参数传入了:

    image-20220117153354363

    下一节说一下事件的绑定,让其他事件,如MouseEnterMouseLeave也能够像按钮的Command一样。

  • 相关阅读:
    【Vue】详解Vue生命周期
    千万不要用window自带文本编辑器编辑配置文件或者代码
    [其它]iOS 13 正式版发布 iPhone 6s或更新型号均可升级
    [转]解决ubuntu16.04 ‘E: 无法获得锁 /var/lib/dpkg/lock-frontend
    [golang]使用gomail发邮件(在Go中发送电子邮件的最佳方式)
    [golang]按图片中心旋转后的新图左顶点和原图左顶点的偏移量计算
    分布式CAP定理,为什么不能同时满足三个特性?
    Java如何运行一个class文件的main方法
    数据库的四大特性以及四个隔离级别和引发的问题
    Redis为什么可以支持那么大的并发访问量?为什么redis没有单点并发瓶颈?
  • 原文地址:https://www.cnblogs.com/choumengqizhigou/p/15813757.html
Copyright © 2020-2023  润新知