TOC

This article has been localized into Russian by the community.

Команды (Commands):

Использование команд

В прошлой статье было много теории о командах и том как они работают. Теперь мы посмотрим, как на практике присвоить команду элементу интерфейса и создать между ними связь.

Начнём с простого примера:

<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. Мы уточнили, какую команду хотим использовать (команда New из ApplicationCommands), а также два обработчика событий. Визуальный интерфейс состоит из одной кнопки. Нашу команду назначили кнопке путём присвоения её свойству кнопки Command.

В коде мы обрабатываем два события. Обработчик CanExecute, который WPF выполнит во время бездействия приложения, чтобы проверить доступность выполнения команды, очень прост в этом примере, так как мы хотим выполнять команду в любой момент. Мы попросту установили в true значение свойства CanExecute, принадлежащее аргументу события.

Когда команда будет вызвана, обработчик Executed покажет окно с сообщением. Если запустить пример и нажать кнопку вы увидите сообщение. Имейте ввиду, что эта команда уже (в качестве бонуса) имеет предустановленные горячие клавиши. Вместо нажатия на кнопку, попробуйте нажать Ctrl+N на клавиатуре - результат тот же.

Использование метода CanExecute

В первом примере, мы написали CanExecute который просто возвращает true, так что кнопка доступна всегда. Конечно так не должно быть для всех кнопок - часто требуется включать и выключать кнопку в зависимости от состояния приложения.

Самый тривиальный пример: когда нужно переключать состояние кнопок Cut(вырезать) и Copy(копировать) в зависимости от наличия выделенного текста, и включать кнопку Paste(вставить) только при наличии скопированного или вырезанного текста. Что мы и сделали в примере:

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

В итоге мы имеем простой интерфейс с парой кнопок и текстовым полем. Первая кнопка вырезает текст, а вторая вставляет.

В коде, мы имеем два события для каждой кнопки: Одни события отвечают за действие, чье имя кончается на _Executed, и события CanExecute. Я описал некую логику для определения возможности выполнения команды и присвоил результат возвращаемому значению CanExecute в EventArgs.

Крутая штука состоит в том, что Вам не нужно вызывать эти методы для обновления кнопок: WPF сделает всю грязную работу за Вас, когда появится свободный момент, гарантируя тем самым, что интерфейс будет обновлён всегда.

Поведение команд по умолчанию и CommandTarget

Как мы видели в прошлом примере, обработка команд может иметь довольно мало кода, с большим количеством методов и стандартной логикой. Вот почему разработчики WPF решили заняться созданием стандартных команд за Вас. По факту, мы можем избежать написания кода в прошлом примере, ведь TextBox может сам позаботиться о командах типа Cut, Copy, Paste, Undo и Redo.

WPF сам обрабатывает события Executed и CanExecute когда текстовое поле вроде TextBox имеет фокус. Вы можете перезаписывать эти события, что мы собственно и сделали в прошлом примере, но если Вам хватит стандартного поведения, Вы можете позволить WPF сделать работу за Вас. Взгляните, насколько проще этот пример:

<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>

Код для этого примера не нужен - WPF сделает всё сам, но только потому что мы хотим использовать эти специфичные команды для этих специфичных элементов управления. TextBox делает работу за нас.

Заметим как я использовал свойства CommandTarget на кнопках что бы привязать команды к нашему TextBox. Это обязательно в данном примере, потому что WrapPanel не управляет фокусом так же как например Toolbar или Menu. Хотя задавать цель для команды всё же стоит.

Сводка

Работа с командами довольно прямолинейна, но требует дополнительную разметку и код. Но награда особенно очевидна когда вам нужно вызывать одно действие из разных мест, или когда вы используете встроенные команды которыми полностью управляет WPF, как видно из последнего примера.

This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!