This article has been localized into Chinese by the community.
使用WPF命令
在上一篇文章中,我们讨论了很多关于命令是什么以及它们如何工作的理论。在本章中,我们将研究如何实际使用命令,将命令分配给用户界面元素并创建将它们链接在一起的命令绑定。
我们将从一个非常简单的例子开始:
<Window x:Class="WpfTutorialSamples.Commands.UsingCommandsSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="UsingCommandsSample" Height="100" Width="200">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="NewCommand_Executed" CanExecute="NewCommand_CanExecute" />
</Window.CommandBindings>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="ApplicationCommands.New">New</Button>
</StackPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace WpfTutorialSamples.Commands
{
public partial class UsingCommandsSample : Window
{
public UsingCommandsSample()
{
InitializeComponent();
}
private void NewCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("The New command was invoked");
}
}
}
我们在 Window.CommandBindings 集合中定义了一个窗口上的命令绑定。它包含了我们希望使用的命令(即 ApplicationCommands 中的 New 命令)以及两个事件处理程序。窗口界面由单个按钮组成,我们将命令附加到该按钮的 Command 属性上。
在Code-behind中,我们处理这两个事件。当应用程序空闲以查看特定命令当前是否可用时,WPF将调用的CanExecute处理程序对于此示例非常简单,因为我们希望此特定命令始终可用。这是通过将事件参数的CanExecute属性设置为true来完成的。
Executed处理程序在调用命令时只显示一个消息框。如果您运行示例并按下按钮,您将看到此消息。需要注意的是,此命令定义了一个默认的键盘快捷键;这是一个额外的好处。您可以尝试按键盘上的Ctrl + N而不是单击按钮 - 结果是相同的。
使用 CanExecute 方法
在第一个示例中,我们实现了一个简单返回true的CanExecute事件,以便该按钮始终可用。但是,对于所有按钮当然不是这样 - 在许多情况下,您希望根据应用程序中的某种状态启用或禁用按钮。
一个非常常见的例子是切换使用Windows剪贴板的按钮,您希望仅在选择文本时启用剪切和复制按钮,并且只有在剪贴板中存在文本时才启用粘贴按钮。这正是我们在这个例子中将要完成的事情:
<Window x:Class="WpfTutorialSamples.Commands.CommandCanExecuteSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandCanExecuteSample" Height="200" Width="250">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Cut" CanExecute="CutCommand_CanExecute" Executed="CutCommand_Executed" />
<CommandBinding Command="ApplicationCommands.Paste" CanExecute="PasteCommand_CanExecute" Executed="PasteCommand_Executed" />
</Window.CommandBindings>
<DockPanel>
<WrapPanel DockPanel.Dock="Top" Margin="3">
<Button Command="ApplicationCommands.Cut" Width="60">_Cut</Button>
<Button Command="ApplicationCommands.Paste" Width="60" Margin="3,0">_Paste</Button>
</WrapPanel>
<TextBox AcceptsReturn="True" Name="txtEditor" />
</DockPanel>
</Window>
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Input;
namespace WpfTutorialSamples.Commands
{
public partial class CommandCanExecuteSample : Window
{
public CommandCanExecuteSample()
{
InitializeComponent();
}
private void CutCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = (txtEditor != null) && (txtEditor.SelectionLength > 0);
}
private void CutCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
txtEditor.Cut();
}
private void PasteCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = Clipboard.ContainsText();
}
private void PasteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
txtEditor.Paste();
}
}
}
所以,我们有这个非常简单的界面,包含有几个按钮和一个TextBox控件。第一个按钮将TextBox控件中选择的内容剪切到剪贴板上,第二个按钮将剪贴板上的内容粘贴到TextBox控件的内容中。
在后端代码中,每个按钮有两个事件:一个执行实际操作,其名称以_Executed结尾,然后是CanExecute事件。在每个CanExecute事件中,您将看到我应用一些逻辑来决定操作是否可以被执行,然后将决定的结果作为返回值赋给EventArgs的CanExecute。
关于这一点很酷的是,您不必调用这些方法来更新按钮 - 当应用程序有空闲时,WPF会自动执行此操作,确保您的界面始终保持更新状态。
默认命令行为和命令目标
正如我们在前面的示例中看到的那样,处理一组命令可能会导致增加相当多的代码,其中包含大量的方法声明和非常标准的逻辑。这可能就是为什么WPF团队决定为您处理一些命令的原因。事实上,在前面例子中,我们可以屏蔽所有的后端处理代码,因为WPF文本框控件可以自动处理常见的命令,如剪切、复制、粘贴、撤消和重做。
当像TextBox这样的文本输入控件具有焦点时,WPF通过为您处理Executed和CanExecute事件来完成此操作。您可以自由地覆盖这些事件,这基本上就是我们在前面的示例中所做的,但如果您只是想要基本行为,您可以让WPF连接命令和TextBox控件并为您完成工作。看看这个例子有多简单:
<Window x:Class="WpfTutorialSamples.Commands.CommandsWithCommandTargetSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandsWithCommandTargetSample" Height="200" Width="250">
<DockPanel>
<WrapPanel DockPanel.Dock="Top" Margin="3">
<Button Command="ApplicationCommands.Cut" CommandTarget="{Binding ElementName=txtEditor}" Width="60">_Cut</Button>
<Button Command="ApplicationCommands.Paste" CommandTarget="{Binding ElementName=txtEditor}" Width="60" Margin="3,0">_Paste</Button>
</WrapPanel>
<TextBox AcceptsReturn="True" Name="txtEditor" />
</DockPanel>
</Window>
此示例不需要Code-behind代码 - WPF为我们处理所有代码,但仅仅是因为我们希望将这些特定命令用于此特定控件。 TextBox为我们工作。
注意我是如何在按钮上使用CommandTarget属性来将命令绑定到我们的TextBox控件的。这在这个特殊的例子中是必需的,因为WrapPanel不像工具栏或菜单那样处理焦点,但是给命令一个目标也是很有意义的。
小结
处理命令非常简单,但确实涉及到一些额外的标记和代码。但是,当您需要从多个地方调用相同的操作时,或者当您使用WPF可以完全处理的内置命令时,正如我们在上一个示例中看到的那样,得到的回报是尤其明显的。