TOC

This article is currently in the process of being translated into Polish (~98% done).

Polecenia:

Używanie poleceń (commands) w WPF

W poprzednim artykule, omawialiśmy sporo teorii na temat czym są polecenia i jak działają. W tym rozdziale przyjrzymy się jak właściwie używać poleceń poprzez przypisanie ich do interfejsu użytkownika oraz tworzenia poleceń wiązania(bindings) które pozwolą na połączenie ich razem.

A więc zacznijmy od bardzo prostego przykładu:

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

Zdefiniowaliśmy polecenie wiązania(binding) w oknie poprzez dodanie do zbioru CommandBindings. Określamy przez to, że polecenie które chcemy użyć(nowe polecenie z ApplicationCommands), a także dwie metody obsługi zdarzeń. Interfejs użytkownika składa się z pojedyńczego przycisku który jest podłączony do polecenia używającego właściwości Command

W module kodu przechwytujemy obsługę dwóch zdarzeń. Obsługa zdarzenia CanExecute, które jest wywoływane przez WPF, kiedy aplikacja przechodzi w stan bezczynności, po to by sprawdzić, czy konkretne polecenie jest aktualnie dostępne, jest bardzo prosta w tym przykładzie, ponieważ chcemy, aby to polecenie było dostępne cały czas. Uzyskujemy to poprzez ustawienie właściwości zdarzenia CanExecute na true.

Program obsługi Executed po prostu wyświetla okno komunikatu, gdy polecenie zostanie wywołane. Jeśli uruchomisz próbkę i naciśniesz przycisk, zobaczysz następujący komunikat komunikat. Warto zauważyć, że to polecenie ma zdefiniowany domyślny skrót klawiaturowy, który otrzymujemy jako dodatkowy bonus. Zamiast klikać przycisk, możesz spróbować nacisnąć Ctrl+N na klawiaturze - rezultat będzie taki sam.

Użycie metody CanExecute

W pierwszym przykładzie zaimplementujemy zdarzenie CanExecute które zwróci wartość True więc przycisk będzie dostępny cały czas. Jednakże to nie dotyczy wszystkich przycisków - w wielu przypadkach będziesz chciał aby przycisk był włączony lub wyłączony w zależności od stanu aplikacji.

Bardzo powszechnym rozwiązaniem jest przełączanie przycisków do korzystania ze schowka systemu Windows. Gdy chcesz żeby przycisk Kopiuj i Wytnij były aktywne tylko wtedy gdy tekst jest zaznaczony natomiast przycisk Wklej był aktywny gdy w schowku znajduje się tekst. To jest dokładnie to co chcemy zrealizować w tym przykładzie:

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

Więc mamy ten prosty interfejs z parą przycisków i kontrolką TextBox. Pierwszy przycisk wycina tekst do schowka a drugi wkleja ze schowka.

W Code-behind mamy dwa zdarzenia dla każdego przycisku: jedno, które wykonuje rzeczywiste działanie i którego nazwa kończy się na _Executed, oraz zdarzenie CanExecute. W obu zdarzeniach można zobaczyć, że zastosowana jest pewna logika, aby zdecydować, czy akcja może być wykonana czy nie, a następnie przypisać ją do wartości zwracanej CanExecute w EventArgs.

Fajnym rozwiązaniem jest to, że nie musisz wywoływać tych metod, aby przyciski były aktualizowane - WPF robi to automatycznie, gdy aplikacja ma chwilę wolnego czasu, zapewniając, że interfejs jest zawsze zaktualizowany.

Domyślne zachowanie Command i CommandTarget

Jak widzieliśmy w poprzednim przykładzie, obsługa zestawu poleceń może prowadzić do dość dużej ilości kodu, z dużą częścią będącej deklaracjami metod i bardzo standardowej logiki. To prawdopodobnie dlatego zespół WPF zdecydował się obsłużyć część z tych rzeczy za Ciebie. W rzeczywistości, moglibyśmy uniknąć całego Code-behind w poprzednim przykładzie, ponieważ WPF TextBox może automatycznie obsługiwać powszechne polecenia, takie jak Wytnij, Kopiuj, Wklej, Cofnij i Powtórz.

WPF robi to poprzez obsługę zdarzeń Executed i CanExecute za Ciebie, gdy kontrolka wejściowa tekstu, taka jak TextBox, ma focus. Masz wolną rękę w nadpisaniu tych zdarzeń, co właściwie zrobiliśmy w poprzednim przykładzie, ale jeśli chcesz tylko podstawowego zachowania, możesz pozwolić WPF połączyć polecenia i kontrolkę TextBox by zrobił pracę za Ciebie. Zobacz, jak wiele prostszy jest ten przykład:

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

Nie jest potrzebny żaden Code-behind dla tego przykładu - WPF radzi sobie z tym wszystkim sam, ale tylko dlatego, że chcemy użyć tych konkretnych poleceń dla konkretnej kontrolki. TextBox wykonuje pracę za nas.

Zauważ, jak używam właściwości CommandTarget na przyciskach, aby powiązać polecenia z naszą kontrolką TextBox. W tym konkretnym przykładzie jest to wymagane, ponieważ WrapPanel nie obsługuje fokusu w taki sam sposób jak np. Toolbar lub Menu, ale także ma to sens, aby nadać poleceniom cel.

Podsumowanie

Radzenie sobie z poleceniami jest dość proste, ale wymaga trochę dodatkowych znaczników i kodu. Jednak opłaca się to robić, gdy musisz wywołać tę samą akcję z wielu miejsc, lub gdy używasz wbudowanych poleceń, które WPF może obsłużyć całkowicie za Ciebie, tak jak widzieliśmy w ostatnim przykładzie.


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!