WPF学习之深入浅出话命令
WPF为我们准备了完善的命令系统,你可能会问:“有了路由事件为什么还需要命令系统呢?”。事件的作用是发布、传播一些消息,消息传达到了接收者,事件的指令也就算完成了,至于如何响应事件送来的消息事件并不做任何限制,每个接收者可已用自己的行为来响应事件。也就是说,事件不具有约束力。命令和事件的区别就在于命令具有约束力。
的确,在实际编程工作中,即使只用事件不用命令程序的逻辑一样被驱动的很好,但我们不能够阻止程序员按照自己的习惯去编写代码。比如保存事件的处理器,程序员可以写Save()、SaveHandle()、SaveDocument()... 这些都符合代码规范。但迟早有一天整个项目会变的让人无法读懂,新来的程序员或修改bug的程序员会很抓狂。如果使用命令,情况就会好很多----当Save命令到达某个组件的时候,命令会自动去调用组件的Save方法。而这个方法可能定义在基类或者接口里(即保证了这个方法是一定存在的),这就在代码结构和命名上做了约束。不但如此,命令还可控制接收者“先做校验,再保存,最后退出”,也就是说命令除了可以约束代码,还可以约束步骤逻辑,让新来的程序员想犯错都难,也让那个修改Bug的程序员容易找到规律,容易上手。
1.1 命令系统的基本元素和关系
- WPF的命令系统由几个基本要素构成,它们是:
- 命令(Command):WPF的命令实际上就是实现了ICommand接口的类,平时使用最多的就是RoutedCommand类。我们还会学习使用自定义命令。
- 命令源(Command Source):即命令的发送者,是实现了ICommandSource接口的类。很多界面元素都实现了这个接口,其中包括Button,ListBoxItem,MenuItem等。
- 命令目标(Command Target):即命令发送给谁,或者说命令作用在谁的身上。命令目标必须是实现了IInputElement接口的类。
- 命令关联(Command Binding):负责把一些外围逻辑和命令关联起来,比如执行之前对命令是否可以执行进行判断、命令执行之后还有哪些后续工作等。
<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里输入然后再单击清除控件,效果如下图:
至此,一个简单的自定义命令就完成了,若想通过Command的CanExecute方法来影响命令源的状态,还需要使用到ICommand和ICommandSource接口的成员组成更复杂的逻辑。
WPF学习之深入浅出话命令相关推荐
- WPF学习之深入浅出话模板
图形用户界面应用程序较之控制台界面应用程序最大的好处就是界面友好.数据显示直观.CUI程序中数据只能以文本的形式线性显示,GUI程序则允许数据以文本.列表.图形等多种形式立体显示. 用户体验在GUI程 ...
- WPF学习之深入浅出话属性
通过前面的学习,我们已经知道Data Binding是WPF"数据驱动UI"理念的基础.上一章我们将主要的精力放在了Binding的数据源这一端,研究了Binding的Source ...
- 深入浅出话命令(Command)-笔记(-)
深入浅出话命令(Command)-笔记(-) 一 基本概念 命令的基本元素: 命令(Command):实现了ICommand接口的类,平常使用最多的是RoutedCommand类. 命令源(Comma ...
- WPF学习第九集-深入浅出话命令
WPF为我们准备了完善的命令系统,你可能会问:"有了路由事件为什么还需要命令系统呢?".事件的作用是发布.传播一些消息,消息传达到了接收者,事件的指令也就算完成了,至于如何响应事件 ...
- .NET-7.WPF学习2. 知识总结
WPF学习2. 知识总结 前言 一.面试 二.代码片段 三.查看链接 前言 对wpf 的知识总结. 一.面试 1. 跨线程操作(Dispatcher)2. template(模板类型[控件模板.数据模 ...
- 【转载】wpf学习笔记1
http://blog.csdn.net/fantasiax/article/details/4575968 深入浅出WPF(7)--数据的绿色通道,Binding(上) 小序: 怎么直接从2蹦到7啦 ...
- 友元 java_深入浅出话友元
深入浅出话友元 小序: 有一阵子没来打扫Blog了--技术这东西,就是走走停停的,一段时间就会遇到一个瓶颈.迷茫一下,然后发现与其因为迷茫而停滞不前,不如瞄准一个大方向勇敢地游下去. 这两天有几个正在 ...
- 【深度学习】深入浅出YOLOv3目标检测算法和实现(图片和视频)
[深度学习]深入浅出YYOLOv3目标检测算法(图片和视频) 文章目录 1 概述 2 一个全卷积神经网络--Darknet-53 3 解释输出 4 代码实现4.1 导入项目4.2 执行脚本4.3 预测 ...
- 深入浅出话多态(上)——具体而微
深入浅出话多态(上)--具体而微 小序 前几天写了一篇<深入浅出话委托>,很多兄弟姐妹发Mail说还算凑合,又有兄弟说能不能写一篇类似的文章,讲解一下什么是"多态".一 ...
最新文章
- 设置Button控件创建完毕时触发的事件.
- 挑战NLP、量子计算难题,300多支本科生队伍同场角逐,2020 ASC超算竞赛一触即发...
- String hashCode 方法为什么选择数字31作为乘子
- Zuul网关原理及源码解读(草稿版)
- Quarkus on OpenJ9 JVM和资源消耗
- 工业以太网在工业控制中的运用
- Linux如何查看所有的用户和组信息
- 页面滚动图片等元素动态加载实现本质
- opencv笔记——cvCreateImage函数说明
- WINHEX Scripts
- linux下codeblocks汉化
- IDEA中鼠标变成矩形块解决
- 人体如何区分阴虚和阳虚?
- Beamforming and Antenna Pattern 2021-11-05
- 有趣好玩的Linux之代码雨效果
- Layer 开启与关闭加载层
- 高盛报告:人工智能、机器学习和数据将推动未来生产力的发展
- VMware12安装win7图文教程
- 【005】基于51单片机的多路热释电红外报警器proteus仿真与实物设计
- Windows系统盘瘦身