第九章 深入浅出话命令

第九章 深入浅出话命令

事件的作用是发布、传播一些消息,消息送达接收者,事件的使命也就完成了,每个接收者使用自己的行为来相应事件。事件不具有约束力,命令具有约束力,不仅可以约束代码,还可以约束步骤逻辑。

  • 命令:ICommand接口的类,常用的是RoutedCommand类,也可以自定义类。
  • 命令源:命令的发送者,ICommandSource接口的类。
  • 命令目标:命令发给谁,或者命令作用在谁身上。IInputElement接口的类。
  • 命令关联:把一些外围逻辑与命令关联起来,如进行逻辑判断、后续处理等。

示例:用Button发送一个命令,当命令到达TextBox时TextBox会被清空(如果TextBox中没有文字则命令不可被发送)。

<Window x:Class="WpfApplication1_2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Background="LightBlue"
        Title="MainWindow" Height="175" Width="260">
    <StackPanel x:Name="stackPanel">
        <Button x:Name="button1" Content="Send Command" Margin="5"/>
        <TextBox x:Name="textBoxA" Margin="5" Height="100"/>
    </StackPanel>
</Window>
namespace WpfApplication1_2
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitializeCommand();//
        }

        //声明并定义命令,一处声明、处处使用
        private RoutedCommand clear_Cmd = new RoutedCommand("Clear", typeof(MainWindow));

        private void InitializeCommand()
        {
            //把命令赋值给发送者(命令源)并指定快捷键
            this.button1.Command = this.clear_Cmd;
            this.clear_Cmd.InputGestures.Add(new KeyGesture(Key.C,ModifierKeys.Alt));

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

            //创建命令关联
            CommandBinding cb = new CommandBinding();
            cb.Command = this.clear_Cmd;//只关注与clear_Cmd相关的事件
            cb.CanExecute += new CanExecuteRoutedEventHandler(cb_CanExecute);
            cb.Executed += new ExecutedRoutedEventHandler(cv_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 cv_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            this.textBoxA.Clear();

            //避免继续向上传而降低程序性能
            e.Handled = true;
        }
    }
}

image-20200827154727977
image-20200827154833450

RoutedCommand只负责在程序中“跑腿”而并不对命令目标做任何操作,由CommandBinding完成操作,CommandBinding一定要设置在命令目标的外围控件上,不然无法捕捉cb_CanExecute、cv_Executed。

WPF 的命令库

WPF类库里的便捷命令库(都是静态类):

  • ApplicationCommands
  • ComponentCommands
  • NavigationCommands
  • MediaCommands
  • EditingCommands

示例:界面上有两个按钮,一个新建Teacher档案、一个新建Student档案。都使用New命令,但通过CommandParameter来区别新建类别的不同。(这里使用XAML为窗体添加CommandBinding、CanExecute、Executed)

<Window x:Class="WpfApplication1_2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="240" Width="360" Background="LightBlue" WindowStyle="ToolWindow">
    <Grid Margin="6">
        <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 Text="Name:"  VerticalAlignment="Center" HorizontalAlignment="Left" Grid.Row="0"/>
        <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>
    <!--下列代码为窗体添加CommandBindings-->
    <Window.CommandBindings>
        <CommandBinding Command="New" CanExecute="New_CanExecute" Executed="New_Executed"/>
    </Window.CommandBindings>
</Window>
namespace WpfApplication1_2
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : 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;
            //如果是Teacher
            if(e.Parameter.ToString()=="Teacher")
            {
                this.listBoxNewItems.Items.Add(string.Format("New Teacher:{0},以其昭昭使人昭昭。", name));
            }

            //如果是Student
            if(e.Parameter.ToString()=="Student")
            {
                this.listBoxNewItems.Items.Add(string.Format("New Student:{0},童蒙!",name));
            }
        }
    }
}
image-20200827162834258
近观命令

ICommand接口与RoutedCommand,这一章节没有仔细看。

自定义Command
  • WPF命令库中没有自己需要的命令,需要声明定义自己的RoutedCommand实例,这是浅层次的理解,本质还是RoutedCommand的使用(WPF自带的命令源和CommandBinding就是专门为RoutedCommand编写)。

  • 从实现ICommand接口开始,定义自己的命令,并把某些业务逻辑也包含在命令中(想使用自己的ICommand派生类就必须连命令源一起实现)。

为了简化CommandBinding的程序结构,将业务逻辑移入命令的Execute方法内。

image-20200827165130415

//自定义接口
public interface IView  //需要将其放在MainWindow的外面,这样其他的窗体才可以引用
{
    //属性
    bool IsChanged { get; set; }

    //方法
    void SetBinding();
    void Refresh();
    void Clear();
    void Save();
    //....
}

//自定义命令源:WPF命令源是为RoutedCommand准备的,且不可重写。只能通过ICommandSource接口创建自己的命令源

namespace WpfApplication1_2
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>

    public class MyCommandSource : UserControl, ICommandSource
    {
        //继承ICommandSource的三个属性:
        public ICommand Command { get; set; }
        public object CommandParameter { get; set; }//本例中没有用到
        public IInputElement CommandTarget { get; set; }//被当作参数传递给了Command的Execute方法

        //在组件被单击时连带执行命令  //为命令选择合适时机,此处为控件被左单击时执行
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);

            //在命令目标上执行命令,或者让命令作用于命令目标
            if (this.CommandTarget != null)
            {
                this.Command.Execute(this.CommandTarget);
            }
        }
    }

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

            //声明命令并使用命令源和目标与之关联
            ClearCommand clearCmd = new ClearCommand();
            this.ctrlClear.Command = clearCmd;
            this.ctrlClear.CommandTarget = this.miniView;
        }


        //自定义命令:实现ICommand接口,创建一个专门作用于IView派生类的命令
        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方法
        }



        //至此,命令和命令源都有了。差一个可用的命令目标。因为此处ClearCommand作用于IView派生类,所以
        //ClearCommand需要实现IView接口:XAML实现UI部分,C#实现接口部分。
    }
}
<Window x:Class="WpfApplication1_2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1_2"
        Title="MainWindow" Height="205" Width="250" Background="LightBlue" WindowStyle="ToolWindow">
    <StackPanel>
        <local:MyCommandSource x:Name="ctrlClear" Margin="10">
            <TextBlock Text="清除" FontSize="16" TextAlignment="Center" Background="LawnGreen" Width="80"/>
        </local:MyCommandSource>
        <local:MiniView x:Name="miniView"/>
    </StackPanel>
</Window>
<UserControl x:Class="WpfApplication1_2.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" 
             mc:Ignorable="d" 
             d:DesignHeight="114" d:DesignWidth="200">
    <Border CornerRadius="5" BorderBrush="LawnGreen" BorderThickness="2">
        <StackPanel>
            <TextBox x:Name="textBox1" Margin="5"/>
            <TextBox x:Name="textBox2" Margin="5,0"/>
            <TextBox x:Name="textBox3" Margin="5"/>
            <TextBox x:Name="textBox4" Margin="5,0"/>
        </StackPanel>
    </Border>
</UserControl>
namespace WpfApplication1_2
{
    /// <summary>
    /// MiniView.xaml 的交互逻辑
    /// </summary>
    
    //自定义命令目标
    public partial class MiniView : UserControl,IView
    {
        public MiniView()
        {
            InitializeComponent();
        }

        //继承自IView的成员们
        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();
        }
    }
}
image-20200827172005749

image-20200827172109503

程序的流程图如下:

image-20200827182413789

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
深入浅出WPF(Windows Presentation Foundation)是一本介绍WPF的书籍,适合初学者。本书共分为以下几章: 第一章:WPF简介 这一章主要介绍WPF的背景和优势,以及与传统的Windows Forms应用程序开发的对比。 第二章:XAML基础 XAML(可扩展应用程序标记语言)是WPF的核心,本章通过一些简单的示例介绍XAML的基本概念和语法。 第三章:WPF控件 WPF提供了丰富的控件,本章依次介绍了常用的Button、TextBox、ComboBox和ListBox等控件的用法和属性。 第四章:布局和容器 WPF的布局和容器可以帮助我们更好地管理和组织控件,本章详细介绍了Grid、StackPanel和WrapPanel等布局和容器的使用。 第五章:数据绑定 数据绑定是WPF的重要特性之一,可以实现数据和UI之间的自动同步,本章通过示例演示了常见的数据绑定方式。 第六章:样式和模板 WPF的样式和模板可以帮助我们更好地定制和美化应用程序的外观,本章介绍了如何定义和应用样式和模板。 第七章:命令和事件 WPF的命令和事件机制是实现交互的重要手段,本章介绍了如何定义和使用命令,以及如何处理事件。 第八章:动画和效果 WPF提供了强大的动画和效果功能,可以使应用程序更加生动和吸引人,本章介绍了常用的动画和效果的实现方式。 第九章:MVVM架构 MVVM(Model-View-ViewModel)是一种经典的软件架构模式,在WPF开发中被广泛应用,本章介绍了MVVM的基本原理和实现方式。 第十章:高级主题 本章涵盖了一些高级的WPF主题,如自定义控件、多文档界面和异步编程等。 通过学习本书,读者可以全面了解WPF的基础知识和常用技术,能够使用WPF开发出功能强大、界面美观的应用程序。无论是从零开始学习WPF,还是希望系统地复习和巩固WPF知识的读者,本书都是一本不可或缺的参考资料。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值