命令与Binding结合运用

开发工具与关键技术:Visual Studio 2017

作者:邓崇富

撰写时间:2019 年 6 月 4 日

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ICommand接口与RoutedCommand:

WPF的命令是实现了ICommand接口的类。ICommand接口非常简单,只包含两个方法和一个事件:

  1. Execute方法:命令执行,或者说命令作用于命令目标之上需要注意的是,显示当中的命令是不会自己“执行”的,他只能“被执行”,而这里,执行变成了命令的方法。
  2. CanExecute方法:在执行之前用来探知命令是否可被执行。
  3. CanExecuteChanged事件:当命令可执行状态发生改变时,可激发此事件来通知其他对象。

RoutedCommand就是这样一个实现ICommand接口的类。RoutedCommand在实现ICommand接口时,并未向Execute和CanExecute方法中添加任何逻辑,也就是说,它是通用的、与具体业务逻辑无关的。

自定义Command:

   说到“自定义命令”,可以分为两个层次来理解。第一个层次比较浅,指的是当WPF命令库中没有包含想要的命令时,我们就的声明定义自己的RoutedCommand实例。第二个层次是指从实现ICommand接口开始,定义自己的命令并且把某些业务逻辑也包含在命令之中。在WPF的命令系统中命令源(包括ButtonBase、MenuItem、ListBoxItem、Hyperlink)、RoutedCommand和CommandBinding三者互相依赖的相当紧密。在源代码级别上,不但没有将命令相关的方法声明为virtual以供我们重写,而且还很多未向程序员公开的逻辑(比如对ExecuteCore和CanExecuteCore这些方法的声明和调用)。换句话说就是,WPF自带的命令源和CommandBinding就是专门为RoutedCommand而编写的,如果我们想使用自己的ICommand派生类就必须连命令源一起实现(即实现ICommandSource接口)。

下面在程序中定义了一个这样的接口:

namespace WPF练习

{

    public interface IView

    {

        //属性

        bool IsChanged { get; set; }

        //方法

        void SetBinding();

        void Refresh();

        void Clear();

        void Save();

    }

}

并且要求每个需要接受命令的组件都必须实现这个接口,这样就确保了命令可以成功地对它执行操作。下面,是实现ICommand接口,创建一个·专门作用于IView派生类的命令。

namespace WPF练习

{

    //自定义命令

    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方法,显然,我们已经把业务逻辑引入了命令的Execute方法中。

  有了自定义命令,因为WPF命令系统的命令源是专门为RoutedCommand准备的并且不能重写,所以我们只能通过实现ICommandSource接口来创建自己的命令源。

代码如下:

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

 

namespace WPF练习

{

    public class MyCommandSource : UserControl, ICommandSource

    {

        //继承自ICommandSource的三个属性

        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(this.CommandTarget);

            }

        }

    }

}

ICommandSource接口只包含Command、CommandParameter和CommandTarget三个属性,至于这三个属性之间有什么关系就要看我们怎么实现了。在本例子中,CommandParameter完全没有被用到,而CommandTarget被当作参数传递给了Command的Execute方法。命令不会自己被发出,所以要为命令的执行选择一个合适的时机,本例中我们在控件被左单击时执行命令。

现在命令和命令源都准备好了,就差一个可用的命令目标。因为我们的ClearCommand专门作用于IView的派生类,所以合格的ClearCommand命令目标必须实现IView接口。设计这种既有UI有需要实现接口的类可以先用XAML编译器实现其UI部分再找到它的后台C#代码实现接口,原理很简单,WPF会自动为UI元素类添加partial关键字修饰,XAML代码会被翻译成类的一部分,后台代码是类的一部分(甚至可以再多添加几个部分),我们可以在后台代码部分指定基类或实现接口,最终这些部分代码会被翻译到一起。

这个组件的XAML部分代码如下:

<UserControl x:Class="WPF练习.MiniView"

           <!--此处省略了引用得命名空间-->

             Height="114" Width="200">

    <Border CornerRadius="5" BorderBrush="LawnGreen" BorderThickness="2">

        <StackPanel>

            <TextBox x:Name="texBox1" Margin="5"/>

            <TextBox x:Name="texBox2" Margin="5,0"/>

            <TextBox x:Name="texBox3" Margin="5"/>

            <TextBox x:Name="texBox4" Margin="5,0"/>

        </StackPanel>

    </Border>

</UserControl>

它的后台代码部分如下:

using System.Windows.Controls;

 

namespace WPF练习

{

    /// <summary>

    /// UserControl1.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.texBox1.Clear();

            this.texBox2.Clear();

            this.texBox3.Clear();

            this.texBox4.Clear();

        }

    }

}

因为上面只演示了命令对Clear方法的调用,所以其它的方法没有具体实现。当Clear方法被调用的时候,它的几个TextBox会被清空。

下面是把自定义的命令

命令源和命令目标集成起来,窗体的XAML代码如下:

<Window x:Class="WPF练习.Window11"

        <!--此处省略了引用的命名空间-->

        xmlns:local="clr-namespace:WPF练习"

        Title="Window11" Height="205" Width="250">

    <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>

</Window>

下面是它的后台代码:

using System.Windows;

namespace WPF练习

{

    /// <summary>

    /// Window11.xaml 的交互逻辑

    /// </summary>

    public partial class Window11 : Window

    {

        public Window11()

        {

            InitializeComponent();

            //声明命令并使用命令源和目标与之关联

            ClearCommand clearComd = new ClearCommand();

            this.ctrlClear.Command = clearComd;

            this.ctrlClear.CommandTarget = this.miniView;

        }

    }

}

运行程序后,在TextBox里输入文字再点击“清空”按钮,效果如下图:

                  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值