WPF之命令

  命令与事件类似,事件用来发布传播一些消息,消息到达接收者,事件的使命就完成了,至于如何响应事件送来的消息事件并不做规定;而命令一旦发出,所有的命令目标都必须执行这个命令,二者的区别就在命令具有约束力而事件没有。

  命令的基本元素

  • 命令(Command):WPF的命令实际就是实现了ICommand接口的类,平时使用最多的是RotuedCommand类;
  • 命令源(Command Source):命令发送者,是实现类ICommandSource接口的类,例如Button、MenuItem、ListBoxItem等;
  • 命令目标(Command Target):命令将作用在谁身上,命令目标必须实现了IInputElement接口;
  • 命令关联(Command Binding):负责把一些外围逻辑与命令关联起来,比如执行前对命令是否可以执行进行判断、命令执行之后进行一些后续工作等。

  命令的使用步骤

  1. 创建命令类:获得一个实现了ICommand的类,如果命令与具体的业务逻辑无关,可以使用WPF类库的RotuedCommand;
  2. 声明命令实例:创建命令类的实例。一般情况下,程序中的某种操作只需要一个命令实例与之对应即可,比如“保存”命令,可以拿同一个实例去命令所有组件完成保存功能,因此命令常使用单例模式;
  3. 指明命令源:指定由谁来发送这个命令。同一个命令可以有多个命令源,如保存命令,可以由菜单中的保存项发送,也可以由工具栏的保存图标发送;
  4. 指定命令目标:命令目标并不是命令的属性而是命令源的属性,指定命令目标是告诉命令源向哪个组件发送命令,无论这个组件是否获得焦点它都会收到这个命令。如果没有为命令源指定目标,则WPF系统认为当前拥有焦点的对象就是命令目标;
  5. 设置命令关联:指定命令针对用户界面的具体区域,可以添加到命令源的外围控件上。

  一个UI组件一旦被命令源盯上,命令源就会不停地向命令目标“投石问路”,命令目标也会不停地发送出可路由的CanExcute附加事件,事件沿着UI元素树向上传递并被命令关联捕捉,命令关联捕捉到这些事件后会把命令能不能发送实时报告给命令。

  如果命令被发送出去并到达命令目标,命令目标就会发送Excute附加事件,事件沿着UI元素树向上传递并被命令关联捕捉,命令关联会完成指定的后续任务。 

  现在我们实现这样一个需求,定义一个命令,使用Button来发送这个命令,当命令到达TextBox时,TextBox的文本会被清空,如果TextBox文本为空则命令不可发送,XAML代码如下:

<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel x:Name="stackpanel">
        <TextBox x:Name="textBox1" Height="23" Margin="10"></TextBox>
        <Button x:Name="button1" Content="发送命令" Height="23" Margin="10"></Button>
    </StackPanel>
</Window>

  后台处理代码如下:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitCmd();
        }

        private RoutedCommand clearCmd = new RoutedCommand("Clear", typeof(MainWindow));
        
        private void InitCmd()
        {
            //指定命令源、绑定快捷键
            this.button1.Command = clearCmd;
            this.clearCmd.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt));

            //指定命令目标
            this.button1.CommandTarget = textBox1;

            //创建命令关联
            CommandBinding cb = new CommandBinding();
            cb.Command = this.clearCmd;
            cb.Executed += cb_Executed;
            cb.CanExecute += cb_CanExecute;

            //把命令关联安置到外围控件
            this.stackpanel.CommandBindings.Add(cb);
        }

        void cb_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(this.textBox1.Text))
                e.CanExecute = false;
            else
                e.CanExecute = true;
            //避免继续向上传而降低程序性能
            e.Handled = true;
        }

        void cb_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.textBox1.Clear();
            e.Handled = true;
        }
    }

  可见,使用命令就不需要自己写代码判断Button是否可用,CanExcute事件触发频率比较高,为了避免降低性能,在处理完后需要把e.handled设置为ture;CommandBinding一定要设置在命令目标的外围控件上,不然无法捕捉到CanExcute和Excute事件。

  命令参数

  WPF有很多命令,如New、Copy等,如果界面上有两个Button,一个用来New一个Teacher档案,一个用来New一个Student档案,该如何解决呢?这时就用到了命令的CommandPrameter属性。

  我们使用WPF系统的预置命令New,XAML代码如下:

<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel x:Name="stackpanel">
         <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <TextBlock Text="Name" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left"></TextBlock>
            <TextBox x:Name="textBox2" Grid.Column="1" Height="23" Margin="5"></TextBox>
        </Grid>
        <Button x:Name="btnTeacher" Content="Teacher" Command="New" CommandParameter="Teacher" Height="23" Margin="10"></Button>
        <Button x:Name="btnStudent" Content="Student" Command="New" CommandParameter="Student" Height="23" Margin="10"></Button>
    </StackPanel>
    <Window.CommandBindings>
        <CommandBinding Command="New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"></CommandBinding>
    </Window.CommandBindings>
</Window>

  CommandBinding的事件处理代码如下:

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(this.textBox2.Text))
                e.CanExecute = false;
            else
                e.CanExecute = true;
            e.Handled = true;
        }

        private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            string name = this.textBox2.Text;
            if(e.Parameter.ToString()=="Teacher")
            {
                this.textBox2.Text = "Hello Teacher";
            }
            if (e.Parameter.ToString() == "Student")
                this.textBox2.Text = "Hello Student";
        }

  两个按钮都使用New命令,但分别使用Teacher和Student作为参数,当TextBox的Text为空时,Button不可用,当输入文本后Button变为可用,单击Button,TextBox会显示不同的内容。

 

  

posted @ 2015-10-15 11:41  『①角硬幣』  阅读(439)  评论(1编辑  收藏  举报