• ICommand接口在WPF开发中的妙用
  • 发布于 2个月前
  • 407 热度
    0 评论
这篇我们介绍一下WPF中比较重要的接口ICommand,也是WPF中一个新的特性,做过WinForm朋友都知道,WinForm开发是基于事件驱动开发模式,比如一个Button有Click事件,当我点击该按钮时,在当前页面会执行具体的业务,这样带来的UI和业务层产生紧密的耦合,WPF也继承了WinForm这一旧的开发模式,同时给我们添加了新的方法,使得UI和后端业务完全隔离,从而UI和业务逻辑解耦,我们来看一下该接口的定义:
 public interface ICommand
 {
        event EventHandler CanExecuteChanged;
        bool CanExecute(object parameter);
        void Execute(object parameter);
 }

在整个MVVM架构中该接口起着非常重要的作用,我们来看一下该接口成员,CanExecuteChanged事件触发通知UI界面做出响应,比如按钮禁用或启用,表示CanExecute该接口返回一个bool值,表示是否执行命令,返回true,命令执行,false命令不执行。我们通过一个简单的例子实现命令绑定,需求比较简单:我们定义一个输入框,如果为空状态,提交按钮禁用,如果有值,按钮启用,点击提交,并将值显示到页面。


我们定义一个DelegateCommand 实现ICommand接口:
public class DelegateCommand : ICommand
  {
        private Action _execute;
        private Func<bool> _canExecute;
        public DelegateCommand(Action executeMethod)
        {
            _execute = executeMethod;
        }
        public DelegateCommand(Action executeMethod, Func<bool> canExecute)
            : this(executeMethod)
        {
            this._canExecute = canExecute;
        }
        public bool CanExecute(object parameter)
        {
            return _canExecute();
        }
        public event EventHandler CanExecuteChanged 
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }

            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }
        public void Execute(object parameter)
        {
            _execute();
        }
   }
接下来我们定义一个ViewMode类,命名为SampleViewModel实现INotifyPropertyChanged接口
 public class SampleViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        private string name = String.Empty;
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                if (value != this.name)
                {
                    this.name = value;
                    NotifyPropertyChanged();
                }
            }
        }
        private string displayName = string.Empty;
        public string DisplayName
        {
            get { return displayName; }
            set
            {
                if (value != this.displayName)
                {
                    displayName = value;
                    NotifyPropertyChanged();
                }
            }
        }
        ICommand delegateCommand;
        public ICommand DelegateCommand
        {
            get
            {
                if (delegateCommand == null)
                {
                    delegateCommand = new DelegateCommand(Execute, CanExecute);
                }
                return delegateCommand;
            }
        }
        public void Execute()
        {
            DisplayName = Name;
        }
        public bool CanExecute()
        {
            return !string.IsNullOrEmpty(Name);
        }
    }
XMLA页面定义:
<Window x:Class="Example_21.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:Example_21"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Name="txtBox" Width="200" Text="{Binding Path=Name,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBlock Grid.Row="1" Width="200" Text="{Binding Path=DisplayName}"></TextBlock>
        <Button Grid.Row="2" x:Uid="btnSend" x:Name="btnSend" FontSize="16" Content="提交" Width="100" Height="50" 
                Margin="0,0,0,5" Command="{Binding DelegateCommand}"  >
            
        </Button>
    </Grid>
</Window>
MainWindow.xaml.cs 文件
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new SampleViewModel();
        }
    }
从上面的例子可以看出我们可以使用控件的Command属性来绑定要执行的命令,我们还可以设置CommandParameter来传递参数,可以把UI上数据传递到后台,WPF整个框架是基于数据驱动,我们可以做到把UI层完全剥离出来,所以说WPF是一个前端和后台可以完全分离的框架。其实走到这里结合我们WPF-18 INotifyPropertyChanged 接口我们有点看到了MVVM开发模式的影子。

用户评论