标签:
出处:http://blog.csdn.net/fwj380891124
WPF为我们准备了完善的命令系统,你可能会问:“有了路由事件为什么还需要命令系统呢?”。事件的作用是发布、传播一些消息,消息传达到了接收者,事件的指令也就算完成了,至于如何响应事件送来的消息事件并不做任何限制,每个接收者可已用自己的行为来响应事件。也就是说,事件不具有约束力。命令和事件的区别就在于命令具有约束力。
的确,在实际编程工作中,即使只用事件不用命令程序的逻辑一样被驱动的很好,但我们不能够阻止程序员按照自己的习惯去编写代码。比如保存事件的处理器,程序员可以写Save()、SaveHandle()、SaveDocument()... 这些都符合代码规范。但迟早有一天整个项目会变的让人无法读懂,新来的程序员或修改bug的程序员会很抓狂。如果使用命令,情况就会好很多----当Save命令到达某个组件的时候,命令会自动去调用组件的Save方法。而这个方法可能定义在基类或者接口里(即保证了这个方法是一定存在的),这就在代码结构和命名上做了约束。不但如此,命令还可控制接收者“先做校验,再保存,最后退出”,也就是说命令除了可以约束代码,还可以约束步骤逻辑,让新来的程序员想犯错都难,也让那个修改Bug的程序员容易找到规律,容易上手。
1.1 命令系统的基本元素和关系

<Window x:Class="WpfApplication1.Window28"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window28" Height="300" Width="300" WindowStyle="ToolWindow">
    <StackPanel Background="LightBlue" x:Name="sp1">
        <Button Content="Send Command" x:Name="btn1" Margin="5"></Button>
        <TextBox x:Name="txtA" Margin="5,0" Height="200"></TextBox>
    </StackPanel>
</Window>
后台代码为:using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace WpfApplication1
{
    /// <summary>
    /// Window28.xaml 的交互逻辑
    /// </summary>
    public partial class Window28 : Window
    {
        public Window28()
        {
            InitializeComponent();
            InitializeCommand();
        }
        //声明并定义命令
        private RoutedCommand rouutedCommand = new RoutedCommand("Clear",typeof(Window28));
        private void InitializeCommand()
        {
            //把命令赋值给命令源,并定义快捷键
            this.btn1.Command = rouutedCommand;
            this.rouutedCommand.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt));
            //指定命令目标
            this.btn1.CommandTarget = txtA;
            //创建命令关联
            CommandBinding commandBinding = new CommandBinding();
            commandBinding.Command = rouutedCommand;//只关注与rouutedCommand相关的命令
            commandBinding.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute);
            commandBinding.Executed += new ExecutedRoutedEventHandler(cb_Execute);
            //把命令关联安置在外围控件上
            this.sp1.CommandBindings.Add(commandBinding);
        }
        //当命令到达目标之后,此方法被调用
        private void cb_Execute(object sender, ExecutedRoutedEventArgs e)
        {
            txtA.Clear();
            //避免事件继续向上传递而降低程序性能
            e.Handled = true;
        }
        //当探测命令是否可执行的时候该方法会被调用
        private void cb_CanExecute(object sender,CanExecuteRoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(txtA.Text))
            {
                e.CanExecute = false;
            }
            else
            {
                e.CanExecute = true;
            }
            //避免事件继续向上传递而降低程序性能
            e.Handled = true;
        }
    }
}
运行程序,在TextBox中输入内容之后,Button在命令可执行状态下变为可用,此时单击按钮或者按Alt+C,TextBox就会被清空,效果如下图:
                                                
private RoutedCommand rouutedCommand = new RoutedCommand("Clear",typeof(Window28));
命令具有一处声明,处处使用的特点,比如Save命令,在程序的任何地方它都表示要求命令目标保存数据。因此,微软在WPF类库里面准备了一些便捷的命令库,这些命令库包括:
<Window x:Class="WpfApplication1.Window29"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window29" Height="278" Width="398">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="24" />
            <RowDefinition Height="4" />
            <RowDefinition Height="24" />
            <RowDefinition Height="4" />
            <RowDefinition Height="24" />
            <RowDefinition Height="4" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <!--命令和命令参数-->
        <TextBlock  HorizontalAlignment="Left" Name="textBlock1" Text="Name:" VerticalAlignment="Center" Grid.Row="0"/>
        <TextBox x:Name="txtName" Margin="60,0,0,0" Grid.Row="0"></TextBox>
        <Button Content="New Teacher" Grid.Row="2" Command="New" CommandParameter="Teacher"></Button>
        <Button Content="New Student" Grid.Row="4" Command="New" CommandParameter="Student"></Button>
        <ListBox Grid.Row="6" x:Name="lbInfos">
            
        </ListBox>
    </Grid>
    <!--为窗体添加CommandBinding-->
    <Window.CommandBindings>
        <CommandBinding Command="New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed">
            
        </CommandBinding>
    </Window.CommandBindings>
</Window>
以上代码有两个地方需要注意:        private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (string.IsNullOrEmpty(txtName.Text))
            {
                e.CanExecute = false;
            }
            else
            {
                e.CanExecute = true;
            }
            //路由终止,提高系统性能
            e.Handled = true;
        }
        private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Parameter.ToString() == "Student")
            {
                this.lbInfos.Items.Add(string.Format("New Student:{0} 好好学习,天天向上。",txtName.Text));
            }
            else if(e.Parameter.ToString()=="Teacher")
            {
                this.lbInfos.Items.Add(string.Format("New Teacher:{0} 学而不厌,诲人不倦。", txtName.Text));
            }
            //路由终止,提高系统性能
            e.Handled = true;
        }
运行程序,当TextBox中没有内容的时候,两个按钮都不可用;当输入文字后按钮变为可用,单击按钮,ListBox中会添加不同的条目,效果如下图:
  
 <Button x:Name="cmdBtn" Command="{Binding Path=ppp, Source=sss}" Content="Command"></Button>
不过话又说回来了,因为大多数命令按钮都有相对应的图标来表示固定的含义,所以日常工作中一个控件的命令一经确定很少改变。




    public interface IView
    {
        //属性
        bool IsChanged { get; set; }
        //方法
        void SetBinding();
        void Refresh();
        void Clear();
        void Save();
    }
并且要求每个接收命令的组件必须实现这个接口,这样可以确保命令可以对其进行操作。    /// <summary>
    ///自定义命令
    /// </summary>
    public class ClearCommand:ICommand
    {
        //用来判断命令是否可以执行
        public bool CanExecute(object parameter)
        {
            throw new NotImplementedException();
        }
        //当命令可执行状态发送改变时,应当被激发
        public event EventHandler CanExecuteChanged;
        //命令执行时,带有与业务相关的Clear逻辑
        public void Execute(object parameter)
        {
            IView view = parameter as IView;
            if(view!=null)
            {
                view.Clear();
            }
        }
    }
命令实现了ICommand接口并继承了CanExecuteChanged事件、CanExecute方法、Execute方法。目前这个命令比较简单,只用到了Execute方法。在实现这个方法时,我们将这个方法唯一的参数作为命令的目标,如果目标是IView接口的派生类则调用其Clear方法---显然我们已经把程序的业务逻辑引入到了命令的Execute方法中。    /// <summary>
    /// MyCommandSource.xaml 的交互逻辑
    /// </summary>
    public partial class MyCommandSource : UserControl,ICommandSource
    {
        
        /// <summary>
        /// 继承自ICommand的3个属性
        /// </summary>
        public ICommand Command
        {
            get;
            set;
        }
        public object CommandParameter
        {
            get;
            set;
        }
        public IInputElement CommandTarget
        {
            get;
            set;
        }
        //在命令目标上执行命令,或者说让命令作用于命令目标
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            if(this.CommandTarget!=null)
            {
                this.Command.Execute(CommandTarget);
            }
        }
    }
ICommand接口只包含Command,CommandParameter,CommandTarget  3个属性,至于这3个属性直接有什么样的关系就看我们要怎么去实现了。在本例中CommandParameter完全没有被用到,而CommandTarget作为参数传递给了Command的Execute方法。命令不会自己被发出,所以一定要为命令的执行选择一个好的时机,本例中我们在控件左单击的时候执行命令。<UserControl x:Class="WpfApplication1.MniView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Border CornerRadius="5" BorderBrush="GreenYellow" BorderThickness="2">
        <StackPanel>
            <TextBox Margin="5" x:Name="txt1"></TextBox>
            <TextBox Margin="5" x:Name="txt2"></TextBox>
            <TextBox Margin="5" x:Name="txt3"></TextBox>
            <TextBox Margin="5" x:Name="txt4"></TextBox>
        </StackPanel>
    </Border>
</UserControl>
它的后台代码部分如下:     /// <summary>
    /// MniView.xaml 的交互逻辑
    /// </summary>
    public partial class MniView : UserControl,IView
    {
        //构造器
        public MniView()
        {
            InitializeComponent();
        }
        //继承自IView的成员们
        public bool IsChanged
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }
        public void SetBinding()
        {
            throw new NotImplementedException();
        }
        public void Refresh()
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// 用于清除内容的业务逻辑
        /// </summary>
        public void Clear()
        {
            this.txt1.Clear();
            this.txt2.Clear();
            this.txt3.Clear();
            this.txt4.Clear();
        }
        public void Save()
        {
            throw new NotImplementedException();
        }
    }
因为我们只演示Clear方法被调用,所以其它几个方法没有具体实现。当Clear方法被调用的时候,它的几个TextBox会被清空。<Window x:Class="WpfApplication1.Window30"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window30" Height="300" Width="300" xmlns:my="clr-namespace:WpfApplication1">
    <StackPanel>
        <my:MyCommandSource x:Name="myCommandSource1">
            <TextBlock Text="清除" Width="80" FontSize="16" TextAlignment="Center" Background="LightGreen"></TextBlock>
        </my:MyCommandSource>
        <my:MniView x:Name="mniView1" />
    </StackPanel>
</Window>
本例中使用简单的文本作为命令源的显示内容,实际工作中可以使用图标,按钮或者更复杂的内容来填充它,但要适当更改激发命令的方法。不然你打算在里面放置一个按钮,那么就不要用OnMouseLeftButtonDown的方法来执行命令了,而应该捕获button的Click事件并在事件处理器中执行方法(Mouse事件会被Button吃掉)。    /// <summary>
    /// Window30.xaml 的交互逻辑
    /// </summary>
    public partial class Window30 : Window
    {
        public Window30()
        {
            InitializeComponent();
            ClearCommand clearCommand = new ClearCommand();
            this.myCommandSource1.Command = clearCommand;
            this.myCommandSource1.CommandTarget = mniView1;
        }
    }
我们首先创建了一个ClearCommand实例并把它赋值给自定义命令源的Command属性,自定义命令源的CommandTarget属性目标是MiniView的实例。提醒一句:为了讲解清晰才把命令放在这里,正规的方法应该是把命令声明为静态全局的地方供所有对象调用。运行程序,在TextBox里输入然后再单击清除控件,效果如下图:

标签:
原文地址:http://www.cnblogs.com/experience/p/4489006.html