-
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");
		}
	}
}

我们通过将命令绑定到其CommandBindings集合来定义Window上的命令绑定。我们指定我们希望使用的Command(来自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控件。第一个按钮将切入剪贴板,第二个按钮将从中粘贴。

在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可以完全为您处理的内置命令时,奖励尤为明显,如上一个示例所示。