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 выполнит во время бездействия приложения что бы проверить доступность выполнения команды, очень прост в этом пример, так как мы хотим выполнять команду в любой момент. Мы попросту установили свойство CanExecute принадлежащее аргументу события на true.

Когда команда будет вызвана, обработчик 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!