TOC

This article has been localized into Polish by the community.

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.

Kiedy wywoływana jest komenda następuje obsługa zdarzenia: "Executed", które po prostu wyświetla okno dialogowe. Jeśli uruchomimy podany przykład i wciśniemy przycisk, pojawi się okienko z komunikatem: "The New command was invoked". Należy wspomnieć że ta komenda daje nam bonus w postaci domyślnego skrótu klawiszowego "Ctrl+N", którego można używać zamiennie z przyciskiem.

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!