WPF学习—命令

摘抄自《深入浅出WPF》第九章

WPF命令系统基本要素:

1.命令(Command):WPF命令实际上就是实现ICommand接口的类,用的最多的是RoutedCommand类

2.命令源(Command Source):命令发送者,是实现了ICommandSource接口的类

3.命令目标(Command Target):命令发送目标。命令目标必须是实现了IInputElement接口的类

4.命令关联(Command Binding):负责把一些外围逻辑与命令关联起来。

 

命令的使用步骤:

1.创建命令类:即获得一个实现ICommand接口的类,如果命令与具体业务逻辑无关则使用WPF类库中的RoutedCommand类即可,否则需要创建RoutedCommand(或ICommand接口)的派生类

2.声明命令实例:创建命令类的实例

3.指定命令源:即指定由谁发送命令。一旦把命令指派给命令源,命令原会受命令的影响,当命令不能被执行的时候作为命令源的控件将处在不可用的状态。

4.指定命令目标:命令目标并不是命令的属性而是命令源的属性,指定命令目标是告诉命令源向哪个组件发送命令,无论这个组件是否拥有焦点它都会收到这个命令。如果没有为命令源指定命令目标,则WPF系统认为当前拥有焦点的对象就是命令目标

5.设置命令关联:WPF命令需要CommandBindng在执行前来帮助判断是不是可以执行,在执行后还要做一些事件。

WPF命令系统基本元素关系图:

 

实例:定义一个命令,使用Button发送,命令送达TextBox时TextBox会被清空(如果TextBox中没有文字则命令不可被送达,Button按钮不可用)

XAML界面:

<Window x:Class="SimpleCommandTest.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:SimpleCommandTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel x:Name="stackPanel" Margin="5" Background="AliceBlue">
            <Button x:Name="button1" Content="Send Command" Margin="5"/>
            <TextBox x:Name="textBoxA" Margin="5" Height="100" TextWrapping="Wrap"/>
        </StackPanel>
    </Grid>
</Window>

后台代码:

 public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitializeCommand();
        }
        //声明并定义命令
        private RoutedCommand clearCmd = new RoutedCommand("Clear", typeof(MainWindow));

        private void InitializeCommand()
        {
            //把命令赋值给命令源
            this.button1.Command = this.clearCmd;
            //指定快捷键
            this.clearCmd.InputGestures.Add(new KeyGesture(Key.C, ModifierKeys.Alt));
            //指定命令目标
            this.button1.CommandTarget = this.textBoxA;
            //创建关联
            CommandBinding cb = new CommandBinding();
            cb.Command = this.clearCmd;
            cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute);
            cb.Executed += new ExecutedRoutedEventHandler(cb_Executed);
            //把命令关联安置在外围控件上
            this.stackPanel.CommandBindings.Add(cb);
        }

        //当探测命令是否可以执行时,调用
        void cb_CanExecute(object sender,CanExecuteRoutedEventArgs e)
        {
            if(string.IsNullOrEmpty(this.textBoxA.Text))
            {
                e.CanExecute = false;
            }
            else
            {
                e.CanExecute = true;
            }
            e.Handled = true;
        }

        //命令送达目标后,调用
        void cb_Executed(object sender,ExecutedRoutedEventArgs e)
        {
            this.textBoxA.Clear();
            e.Handled = true;
        }
    }

 

WPF命令库:

1.ApplicationCommands

2.ComponentCommands

3.NavigationCommands

4.MediaCommands

5.EditingCommands

 

带参数的命令:通过参数来决定命令要做什么事。命令源一定是实现了ICommandSource接口的对象,而ICommandSource有一个名为CommandPrameter的属性。

实例:

XAML代码

<Window x:Class="SimpleCommandPrameterTest.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:SimpleCommandPrameterTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid Margin="5" Background="AliceBlue">
        <Grid.RowDefinitions>
            <RowDefinition Height="24"/>
            <RowDefinition Height="4"/>
            <RowDefinition Height="24"/>
            <RowDefinition Height="4">
            </RowDefinition>
            <RowDefinition Height="24"/>
            <RowDefinition Height="4"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock Text="Name:" VerticalAlignment="Center" HorizontalAlignment="Left" />
        <TextBox x:Name="nameTextBox" Margin="60,0,0,0" Grid.Row="0"/>
        <Button Content="New Teacher" Command="New" CommandParameter="Teacher" Grid.Row="2"/>
        <Button Content="New Student" Command="New" CommandParameter="Student" Grid.Row="4"/>
        <ListBox x:Name="listBoxNewItems" Grid.Row="6"/>
    </Grid>
    <!--为窗口添加CommandBinding-->
        <Window.CommandBindings>
        <CommandBinding Command="New" CanExecute="New_CanExecute" Executed="New_Executed"/>
    </Window.CommandBindings>
</Window>

后台代码:

 public MainWindow()
        {
            InitializeComponent();
        }

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


        private void New_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            string name = this.nameTextBox.Text;
            if(e.Parameter.ToString()=="Teacher")
            {
                this.listBoxNewItems.Items.Add(string.Format("New Teacher:{0},学而不厌,诲人不倦。",name));
            }
            if(e.Parameter.ToString()=="Student")
            {
                this.listBoxNewItems.Items.Add(string.Format("New Student:{0},好好学习,天天向上。",name));
            }
            this.nameTextBox.Clear();
        }
    }

在XAML中定义了命令不同的参数,参数不同命令执行后结果不同。

 

自定义命令

首先创建一个WPF工程,在MainWindow.xaml.cs中定义一个接口:

public interface IView
    {
        bool IsChanged { get; set; }

        void SetBinding();
        void Refresh();
        void Clear();
        void Save();
    }

每个需要接受命令的组件必须实现这个接口,确保命令可以成功地对它们执行操作

实现ICommand接口:

public class ClearCommand:ICommand
    {
        //当命令可执行状态发生改变时,应当被激发
        public event EventHandler CanExecuteChanged;

        //用于判断命令是否可执行(为了方便,暂不实现)
        public bool CanExecute(object parameter)
        {
            throw new NotImplementedException();
        }

        //命令执行,带有与业务相关的Clear逻辑
        public void Execute(object parameter)
        {
            IView view = parameter as IView;
            if(view!=null)
            {
                view.Clear();
            }
        }
   

    }

命令实现了ICommand接口并继承了CanExecuteChanged事件、CanExecute方法和Execute方法。此命令只用到了Execute方法,实现这个方法时,将方法的参数作为命令的目标,如果目标是IView接口派生类,则调用Clear方法

通过实现ICommandSource接口创建自定义命令源:

 public class MyCommandSource : UserControl, ICommandSource
    {
        //继承自ICommandSource的三个属性
        public ICommand Command { get; set; }
        public object CommandParameter { get; set; }
        public IInputElement CommandTarget { get; set; }


        //组件被单击时连带执行命令
        protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnPreviewMouseLeftButtonDown(e);
            //在命令目标上执行命令,或称让命令作用于命令目标
            if (this.CommandTarget != null)
            {
                this.Command.Execute(this.CommandTarget);
            }
        }
    }

ICommandSource接口包含Command、CommandParameter、CommandTarget三个属性。在此,CommandParameter没有用到,而CommandTarget被当做参数传递给了Command的Execute方法。

创建命令目标:因为ClearCommand命令作用于IView派生类,所以命令目标必须实现IView接口。为工程添加一个用户控件(UserControl)

XAML代码如下:

<UserControl x:Class="UserCommandTest.MiniView"
             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" 
             xmlns:local="clr-namespace:UserCommandTest"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <TextBox x:Name="textBox1" Margin="5"/>
        <TextBox x:Name="textBox2" Margin="5"/>
        <TextBox x:Name="textBox3" Margin="5"/>
        <TextBox x:Name="textBox4" Margin="5"/>
    </StackPanel>
</UserControl>

后台代码如下(此类需要继承自IView):

//此类需要继承IView
    public partial class MiniView : UserControl,IView
    {
        public MiniView()
        {
            InitializeComponent();
        }

        public bool IsChanged { get; set; }
        public void SetBinding(){ }
        public void Refresh() { }
        public void Save() { }

        public void Clear()
        {
            this.textBox1.Clear();
            this.textBox2.Clear();
            this.textBox3.Clear();
            this.textBox4.Clear();
        }
    }

主窗体的XAML代码:

<Window x:Class="UserCommandTest.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:UserCommandTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <StackPanel>
            <local:MyCommandSource x:Name="ctrlClear" Margin="10">
                <TextBlock Text="清除" FontSize="16" TextAlignment="Center" Background="LightGreen" Width="80"/>
            </local:MyCommandSource>
            <local:MiniView x:Name="miniView"/>
        </StackPanel>
    </Grid>
</Window>

主窗体后台代码:

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

            ClearCommand clearCmd = new ClearCommand();
            this.ctrlClear.Command = clearCmd;
            this.ctrlClear.CommandTarget = this.miniView;
        }
    }

先创建一个ClearCommand命令实例,并赋值给自定义命令源的Command属性,自定义命令源的CommandTarget属性是自定义命令目标MiniView实例。

 

posted @ 2018-10-16 21:00  梦九龍  阅读(425)  评论(0编辑  收藏  举报