WPF学习(第九章)命令
来源:互联网 发布:mac怎么切换独立显卡 编辑:程序博客网 时间:2024/06/05 15:05
1. WPF命令
假设有一个程序,该程序包含了一个应用程序方法PrintDocument()。可以使用4中方式触发该方法:通过主菜单、右键菜单、键盘快捷键和工具栏按钮。在应用程序生命周期的特定时刻,需要暂时禁用PrintDocument()功能。这意味着需要禁用两个菜单命令、一个工具栏命令、忽略快捷键。编码完成这些工作是很麻烦的。更糟糕的是,如果没有正确完成这项工作,可能会使不同状态的代码块不正确的重叠,导致某个控件在不应该可用时被启用。调试这类代码是很枯燥的。
WinForm没有提供任何机制解决上述问题,而WPF采用命令机制解决了上述问题。
WPF的命令模型增加了两个特性:
1.将事件委托给适当的命令;
2.使控件的启用状态跟命令的状态保持同步。
2. WPF命令模型
命令模型包含4个重要元素:
1.命令:表示一个程序任务,例如常用的New、Open、Cut、Paste等。也可以是自定义命令的如用于计算数据的Calculate。
2.命令绑定:每个命令可以被多个地方绑定(作用范围),并且可以在不同地方有不同事件逻辑(具体程序)。例如有两块文本编辑区域,Cut、Copy、Paste命令分别对它们绑定。
3.命令源:触发命令的源。例如按钮、菜单项、工具栏项等。
4.命令目标:执行命令的目标控件。例如Cut、Copy、Paste命令绑定的目标是一个文本编辑区域。
ICommand:它是命令类的基类接口,其中Execute()将包含应用程序任务逻辑,CanExecute返回命令是否可用的状态,当状态改变时会触发事件CanExecuteChanged。
RoutedCommand:所有WPF命令都是继承该类。在ICommand基础上增加了Name(命令名称标识)、OwnerType(命令所属类)、InputGestures(命令快捷键的集合)。函数里的target表示执行命令的目标。
RoutedUICommand:用于具有文本的命令。事实上命令库提供的所有命令都是继承它的。
public interface ICommand{ voidExecute(object parameter); boolCanExecute(object parameter); eventEventHandler CanExecuteChanged;}public class RoutedCommand : ICommand{ Name; OwnerType; InputGestures; voidExecute(object parameter, IInputElement target); boolCanExecute(object parameter, IInputElement target); eventEventHandler CanExecuteChanged;}public class RoutedUICommand :RoutedCommand{ Text; ...}
3. 命令库
命令库中包含了超过100条常用的命令,通过5个静态类的静态属性提供。
命令类
示例命令
ApplicationCommands
New、Open、Close、Cut、Copy、Paste、Save、Print
NavigationCommands
BrowseForward、BrowseBack、Zoom、Search
EditingCommands
AlignXXX、MoveXXX、SelectXXX
MediaCommands
Play、Pause、NextTrack、IncreaseVolume、Record、Stop
ComponentCommands
MoveXXX、SelectXXX、ScrollXXX、ExtendSelectionXXX
例如:ApplicationCommands.Open是一个RoutedUICommand的静态变量。根据绑定的源的不同决定在用户界面什么地方触发。命令库中的很多命令都有默认的快捷键绑定,如Open命令的InputGestures包含了Ctrl+O,OwnerType是ApplicationCommands。
4. 执行命令
很多控件都实现了ICommandSource接口,ICommandSource包含三个属性:Command、CommandParameter、CommandTarget。也就是说很多控件都有这三个属性。
Command:它连接一个已存在的命令,例如命令库的Open等,或我们自定义的命令。
CommandParameter:希望随命令一起发送的数据。
CommandTarget:命令目标。
例如:Command连接到ApplicationCommands.Open的代码:
<ButtonCommand="ApplicationCommands.Open">Open</Button>
或者省略命令库的类名:
<ButtonCommand="Open">Open</Button>
其中Button就是命令源。
在XAML里绑定命令:
<Grid Name=”grid1”> <Grid.CommandBindings> <CommandBinding Command="ApplicationCommands.Open"Executed="CommandBinding_Executed"></CommandBinding> </Grid.CommandBindings> <Button Command="ApplicationCommands.Open">Open</Button></Grid>
在C#代码里绑定命令:
CommandBinding binding = newCommandBinding(ApplicationCommands.Open);binding.Executed +=CommandBinding_Executed;grid1.CommandBindings.Add(binding);
可见,命令绑定的主要是内容包括两个:绑定一个事件处理函数,绑定一个区域(grid1)。
事件处理函数为:
private void CommandBinding_Executed(objectsender, ExecutedRoutedEventArgs e)
其中通过参数e可以传递很多信息。如绑定的命令是什么、命令的参数等。
如果命令源是菜单项。则连接命令后,会把命令的Text赋值给菜单项的文本,并把快捷键加入菜单项。
<Menu><MenuItemHeader="File"><MenuItem Command="Open"></MenuItem></MenuItem></Menu>
最初的例子:
现在我们回到最初的问题,当多个控件对应相同的命令时,命令的禁用功能会影响到所有控件的禁用状态。
下面的程序包含一个菜单栏,一个工具栏和一个TextBox。平时Save为灰色不可用。当TextBox的文本内容变化时,启用Save功能。
首先我们创建命令绑定:
CommandBinding binding = newCommandBinding(ApplicationCommands.Save);binding.Executed += Save_Executed;binding.CanExecute +=Save_CanExecute;stackpanel1.CommandBindings.Add(binding);
绑定的区域是stackpanel,也就是说stackpanel里所有连接到该命令的子控件都执行Executed事件处理函数,并且它们的启用状态受CanExecute的影响。
我们定义一个布尔变量isDirty表示如果TextBox的文本内容如果改变了没保存就是“dirty”。bool isDirty = false;
void Save_CanExecute(object sender,CanExecuteRoutedEventArgs e){e.CanExecute =isDirty;} void Save_Executed(object sender,ExecutedRoutedEventArgs e){//do somethingisDirty = false;}void TextBox_TextChanged_1(object sender,TextChangedEventArgs e){isDirty = true;}
XAML代码如下:
<StackPanel Name="stackpanel1"> <Menu> <MenuItem Header="File"> <MenuItemHeader="Open"></MenuItem> <MenuItemHeader="Save" Command="Save"></MenuItem> </MenuItem> </Menu> <ToolBar Name="toolbar1"> <Button>Open</Button> <Button Command="Save">Save</Button> <Button Click="Button_Click_1">手动禁用Save</Button> </ToolBar> <TextBox Height="100"TextWrapping="WrapWithOverflow" Text="text"AcceptsReturn="True"TextChanged="TextBox_TextChanged_1"/></StackPanel>
上面的代码可以很好的满足我们的需求,但是在更深层次上并不太好理解。TextBox并没有连接到Save命令,它只是单纯的修改isDirty的值而已,Save_CanExecute就会被触发。看起来就好像是Save_CanExecute经常会被触发的样子。事实上只要stackpanel1里的任意控件状态变化都会触发stackpanel1绑定的所有命令的CanExecute,而不论其是否连接到该命令。
5. 内置命令
一些文本输入控件(TextBox)具有内置的命令处理事件(Cut、Copy、Paste等)。所以我们可以在TextBox里用键盘组合键来复制(Ctrl+C)粘贴(Ctrl+V),而我们并没有写任何相关代码。此外,我们可以在菜单栏和工具栏里使用类似下面的代码,就可以实现对应的功能。
<MenuItem Header="Cut" Command="Cut"></MenuItem>
上面代码会绑定TextBox的内置事件,因此不用我们自己写Cut的详细实现。然而,只能在菜单栏和工具栏使用才有效,因为菜单栏和工具栏默认实现了CommandTarget属性为当前获取焦点的控件。也就是说当前TextBox获取焦点,菜单栏的CommandTarget就指定了TextBox。如果我们想要在菜单栏和工具栏之外的控件里实现Cut功能怎么办呢?可以用下面的代码:
<Button Command="Cut"CommandTarget="{Binding ElementName=textbox}"> Cut</Button>
上面代码需要手动指定CommandTarget为TextBox,这样就可以实现点击按钮来剪切TextBox文本的功能了。
6. 自定义命令
通过实例化一个新的RoutedUICommand对象来实现自定义命令。最好的方式是模仿命令库的方式创建静态命令。
下面看一个例子:
模仿命令库里的命令,我们自定义一个名称为MyCommands.MyCalculate的命令,它的Text是"My Calculate",Name是"MyCalculate",对应的快捷键是Ctrl+T。具体代码如下:
public class MyCommands { private static RoutedUICommand myCalculate; static MyCommands() { InputGestureCollection inputGestures = new InputGestureCollection(); inputGestures.Add(new KeyGesture(Key.T, ModifierKeys.Control, "Ctrl+T")); myCalculate = new RoutedUICommand("My Calculate","MyCalculate", typeof(MyCommands), inputGestures); } public static RoutedUICommand MyCalculate { get { return myCalculate; } }}
我们打算用这个命令完成一个简单的数学计算任务——加法运算。
XAML代码如下:
<Windowx:Class="WpfApplication10.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication10" Title="MainWindow"Height="350" Width="525"> <Grid Height="Auto"VerticalAlignment="Top"> <Grid.ColumnDefinitions> <ColumnDefinition Width="100"></ColumnDefinition> <ColumnDefinition Width="30"></ColumnDefinition> <ColumnDefinition Width="100"></ColumnDefinition> <ColumnDefinition Width="30"></ColumnDefinition> <ColumnDefinition Width="100"></ColumnDefinition> <ColumnDefinition Width="100"></ColumnDefinition> </Grid.ColumnDefinitions> <Grid.CommandBindings> <CommandBinding Command="local:MyCommands.MyCalculate"Executed="CommandBinding_Executed_1"CanExecute="CommandBinding_CanExecute_1"></CommandBinding> </Grid.CommandBindings> <TextBox Grid.Column="0"Height="30" Margin="3" Name="textbox1"></TextBox> <LabelGrid.Column="1">+</Label> <TextBox Grid.Column="2"Height="30" Margin="3"Name="textbox2"></TextBox> <LabelGrid.Column="3">=</Label> <TextBox Grid.Column="4"Height="30" Margin="3"Name="textbox3"></TextBox> <Button Grid.Column="5"Command="local:MyCommands.MyCalculate">Calculate</Button> </Grid></Window>
具体的事件响应函数代码如下:
<p>private voidCommandBinding_Executed_1(object sender, ExecutedRoutedEventArgs e)</p><p> {</p><p> int value = Int32.Parse(textbox1.Text)+ Int32.Parse(textbox2.Text);</p><p> textbox3.Text = value.ToString();</p><p> }</p><p> </p><p> private voidCommandBinding_CanExecute_1(object sender, CanExecuteRoutedEventArgs e)</p><p> {</p><p> if (textbox1.Text == ""|| textbox2.Text == "")</p><p> e.CanExecute = false;</p><p> else</p><p> e.CanExecute = true;</p> }
- WPF学习(第九章)命令
- WPF学习第九集-深入浅出话命令
- (WPF学习记录)第九章 Routed输入事件
- WPF and Silverlight 学习笔记(十五):WPF命令(Commands)
- WPF and Silverlight 学习笔记(十五):WPF命令(Commands)
- WPF学习一:命令
- python学习(第九章)
- 第九章、sed 命令
- 第九章 dd命令
- WPF(命令)
- WPF(命令参数)
- WPF(命令)
- WPF学习笔记(3):Path绘制命令
- perl学习-第九章
- python学习第九章
- 第九章 打包命令: tar
- WPF学习之【事件,命令和设置】
- WPF学习系列029: 3.4 命令
- Spring如何使用aop切入controller层---问题笔记
- Window8 FTP站点构建
- 关于Certificate、Provisioning Profile、App ID的介绍及其之间的关系
- 第11周项目1.2用函数输出星号图
- C++中#ifdef、#endif等宏的使用
- WPF学习(第九章)命令
- 蓝桥杯begin练习
- IOC及Bean容器
- Json & Gson
- crackme.chm之figugegl.1.exe
- 第十一周项目 6 回文,素数(反序数)
- glusterfs双副本原理解析和脑裂解决方案
- SAP ABAP编程 Ranges用法
- Javamail入门