This article has been localized into Polish by the community.
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.