在上一篇文章中,我们讨论了很多关于命令是什么以及它们如何工作的理论。在本章中,我们将研究如何实际使用命令,将命令分配给用户界面元素并创建将它们链接在一起的命令绑定。
我们将从一个非常简单的例子开始:
<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");
}
}
}
我们通过将命令绑定到其CommandBindings集合来定义Window上的命令绑定。我们指定我们希望使用的Command(来自ApplicationCommands的New命令),以及两个事件处理程序。可视界面由一个按钮组成,我们将命令附加到Command属性。
在Code-behind中,我们处理这两个事件。当应用程序空闲以查看特定命令当前是否可用时,WPF将调用的CanExecute处理程序对于此示例非常简单,因为我们希望此特定命令始终可用。这是通过将事件参数的CanExecute属性设置为true来完成的。
Executed处理程序在调用命令时只显示一个消息框。如果您运行示例并按下按钮,您将看到此消息。需要注意的是,此命令定义了一个默认的键盘快捷键,您可以获得额外的奖励。您可以尝试按键盘上的Ctrl + N而不是单击按钮 - 结果是相同的。
在第一个示例中,我们实现了一个简单返回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控件。第一个按钮将切入剪贴板,第二个按钮将从中粘贴。
在Code-behind中,每个按钮有两个事件:一个执行实际操作,一个名称以_Executed结尾,然后是CanExecute事件。在每个中,您将看到我应用一些逻辑来决定是否可以执行该操作,然后将其分配给EventArgs上的返回值CanExecute。
关于这一点很酷的是,您不必调用这些方法来更新按钮 - 当应用程序有空闲时,WPF会自动执行此操作,确保您的界面始终保持更新状态。
正如我们在前面的例子中看到的那样,处理一组命令可能导致相当多的代码,其中很多是方法声明和非常标准的逻辑。这可能就是为什么WPF团队决定为你处理它的原因。实际上,我们可以避免前一个示例中的所有Code-behind,因为WPF TextBox可以自动处理常见命令,如剪切,复制,粘贴,撤消和重做。
当像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可以完全为您处理的内置命令时,奖励尤为明显,如上一个示例所示。