Udostępnij za pośrednictwem


Ogólny przegląd

Commanding to mechanizm wejściowy w programie Windows Presentation Foundation (WPF), który zapewnia obsługę danych wejściowych na bardziej semantycznym poziomie niż dane wejściowe urządzenia. Przykłady poleceń to kopiowanie , wycinanie i wklejanie , które można znaleźć w wielu aplikacjach.

To omówienie definiuje polecenia w WPF, które klasy są częścią modelu poleceń oraz jak używać i tworzyć polecenia w aplikacjach.

Ten temat zawiera następujące sekcje:

Co to są polecenia

Polecenia mają kilka celów. Pierwszym celem jest oddzielenie semantyki i obiektu, który wywołuje polecenie z logiki wykonującej polecenie. Dzięki temu wiele różnych źródeł może wywołać tę samą logikę poleceń i umożliwia dostosowanie logiki poleceń dla różnych obiektów docelowych. Na przykład operacje edycji Kopiuj, Wytniji Wklej, które znajdują się w wielu aplikacjach, mogą być wywoływane przy użyciu różnych akcji użytkownika, jeśli są zaimplementowane za pomocą poleceń. Aplikacja może zezwolić użytkownikowi na wycinanie zaznaczonych obiektów lub tekstu przez kliknięcie przycisku, wybranie elementu w menu lub użycie kombinacji, takiej jak CTRL+X. Za pomocą poleceń można powiązać każdy typ akcji użytkownika z tą samą logiką.

Innym celem poleceń jest wskazanie, czy jest dostępna akcja. Aby kontynuować przykład cięcia obiektu lub tekstu, akcja ma sens tylko wtedy, gdy coś jest zaznaczone. Jeśli użytkownik spróbuje wyciąć obiekt lub tekst bez wybrania niczego, nic się nie stanie. Aby wskazać to użytkownikowi, wiele aplikacji wyłącza przyciski i elementy menu, aby użytkownik wiedział, czy można wykonać akcję. Polecenie może wskazywać, czy akcja jest możliwa przez zaimplementowanie metody CanExecute. Przycisk może subskrybować zdarzenie CanExecuteChanged i być wyłączony, jeśli CanExecute zwraca false, lub być włączony, jeśli CanExecute zwraca true.

Semantyka polecenia może być spójna w aplikacjach i klasach, ale logika akcji jest specyficzna dla określonego obiektu, na który działał. Kombinacja CTRL+X wywołuje polecenie Cut w klasach tekstowych, klasach obrazów i przeglądarkach sieci Web, ale rzeczywista logika wykonywania operacji wycinania jest definiowana przez aplikację wykonującą wycinanie. RoutedCommand pozwala klientom na wdrożenie logiki. Obiekt tekstowy może wyciąć zaznaczony tekst do schowka, podczas gdy obiekt obrazu może wyciąć wybrany obraz. Gdy aplikacja obsługuje zdarzenie Executed, ma dostęp do obiektu docelowego polecenia i może podjąć odpowiednie działania w zależności od typu obiektu docelowego.

Prosty przykład polecenia w WPF

Najprostszym sposobem użycia polecenia w WPF jest użycie wstępnie zdefiniowanej RoutedCommand z jednej z klas bibliotek poleceń; należy użyć kontrolki, która ma natywną obsługę polecenia; i użyj kontrolki, która ma natywną obsługę wywoływania polecenia. Polecenie Paste jest jednym ze wstępnie zdefiniowanych poleceń w klasie ApplicationCommands. Kontrolka TextBox ma wbudowaną logikę do obsługi polecenia Paste. Klasa MenuItem ma natywną obsługę wywoływania poleceń.

W poniższym przykładzie pokazano, jak skonfigurować MenuItem tak, aby po kliknięciu wywołało polecenie Paste na TextBox, zakładając, że TextBox ma fokus klawiatury.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

Cztery główne pojęcia w systemie poleceń WPF

Model poleceń kierowanych w WPF można podzielić na cztery główne pojęcia: polecenie, źródło polecenia, cel polecenia i powiązanie polecenia.

  • Polecenie jest akcją do wykonania.

  • Źródło polecenia jest obiektem, który wywołuje polecenie.

  • Obiektem docelowym polecenia jest obiekt, na który jest wykonywane polecenie.

  • Powiązanie polecenia to obiekt, który mapuje logikę polecenia do polecenia.

W poprzednim przykładzie polecenie Paste jest poleceniem, MenuItem jest źródłem poleceń, TextBox jest obiektem docelowym polecenia, a powiązanie polecenia jest dostarczane przez kontrolkę TextBox. Warto zauważyć, że nie zawsze jest tak, że CommandBinding jest dostarczana przez kontrolkę, która jest klasą docelową polecenia. Dość często CommandBinding musi zostać utworzony przez dewelopera aplikacji, lub CommandBinding może być dołączony do przodka obiektu docelowego polecenia.

Polecenia

Polecenia w WPF są tworzone przez zaimplementowanie interfejsu ICommand. ICommand uwidacznia dwie metody, Executei CanExecuteoraz zdarzenie CanExecuteChanged. Execute wykonuje akcje skojarzone z poleceniem . CanExecute określa, czy polecenie może zostać wykonane na bieżącym obiekcie docelowym polecenia. CanExecuteChanged jest wywoływany, jeśli menedżer poleceń, który centralizuje operacje poleceń wykrywa zmianę w źródle poleceń, które może unieważnić polecenie, które zostało zgłoszone, ale nie zostało jeszcze wykonane przez powiązanie polecenia. Implementacja WPF ICommand to klasa RoutedCommand i jest centralnym punktem tego omówienia.

Głównymi źródłami danych wejściowych w WPF są mysz, klawiatura, atrament i kierowane polecenia. Bardziej zorientowane na urządzenie dane wejściowe używają RoutedEvent do powiadamiania obiektów na stronie aplikacji o wystąpieniu zdarzenia wejściowego. RoutedCommand nie różni się. Metody Execute i CanExecuteRoutedCommand nie zawierają logiki aplikacji dla polecenia, ale raczej zgłaszają zdarzenia kierowane, które przepływają w dół i w górę przez drzewo elementów, dopóki nie napotkają obiektu z CommandBinding. CommandBinding zawiera programy obsługi dla tych zdarzeń i są to programy obsługi wykonujące polecenie. Aby uzyskać więcej informacji na temat routingu zdarzeń w WPF, zobacz Routed Events Overview.

Metoda Execute w RoutedCommand zgłasza zdarzenia PreviewExecuted i Executed w obiekcie docelowym komendy. Metoda CanExecute na RoutedCommand wywołuje zdarzenia CanExecute i PreviewCanExecute na obiekcie docelowym polecenia. Te zdarzenia tunelują i bąbelkują przez drzewo elementów, dopóki nie napotkają obiektu, który ma CommandBinding dla tego konkretnego polecenia.

WPF udostępnia zestaw typowych poleceń kierowanych w kilku klasach: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommandsi EditingCommands. Te klasy składają się tylko z obiektów RoutedCommand, a nie logiki implementacji polecenia. Logika implementacji to odpowiedzialność za obiekt, na którym jest wykonywane polecenie.

Źródła poleceń

Źródłem polecenia jest obiekt, który wywołuje polecenie. Przykłady źródeł poleceń to MenuItem, Buttoni KeyGesture.

Źródła poleceń w WPF zwykle implementują interfejs ICommandSource.

ICommandSource uwidacznia trzy właściwości: Command, CommandTargeti CommandParameter:

Klasy WPF implementujące ICommandSource to ButtonBase, MenuItem, Hyperlinki InputBinding. ButtonBase, MenuItemi Hyperlink wywołują polecenie po kliknięciu, a InputBinding wywołuje polecenie, gdy związane z nim InputGesture jest wykonywane.

W poniższym przykładzie pokazano, jak używać MenuItem w ContextMenu jako źródła poleceń dla polecenia Properties.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;
Dim cmdSourcePanel As New StackPanel()
Dim cmdSourceContextMenu As New ContextMenu()
Dim cmdSourceMenuItem As New MenuItem()

' Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem)

' Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties

Zazwyczaj źródło polecenia nasłuchuje zdarzenia CanExecuteChanged. To zdarzenie informuje źródło polecenia, że możliwość wykonywania polecenia na bieżącym celu polecenia mogła ulec zmianie. Źródło polecenia może wykonywać zapytania dotyczące bieżącego stanu RoutedCommand przy użyciu metody CanExecute. Źródło polecenia może następnie wyłączyć się, jeśli polecenie nie może zostać wykonane. Przykładem tego jest MenuItem, które staje się wyszarzone, gdy polecenie nie może zostać wykonane.

InputGesture można użyć jako źródła poleceń. Dwa typy gestów wejściowych w WPF to KeyGesture i MouseGesture. Możesz traktować KeyGesture jako skrót klawiaturowy, taki jak CTRL+C. KeyGesture składa się z Key i zestawu ModifierKeys. MouseGesture składa się z MouseAction i opcjonalnego zestawu ModifierKeys.

Aby InputGesture działał jako źródło polecenia, musi być skojarzony z poleceniem. Istnieje kilka sposobów, aby to osiągnąć. Jednym ze sposobów jest użycie InputBinding.

W poniższym przykładzie pokazano, jak utworzyć KeyBinding między KeyGesture a RoutedCommand.

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);
Dim OpenKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

Dim OpenCmdKeybinding As New KeyBinding(ApplicationCommands.Open, OpenKeyGesture)

Me.InputBindings.Add(OpenCmdKeybinding)

Innym sposobem skojarzenia InputGesture z RoutedCommand jest dodanie InputGesture do InputGestureCollection na RoutedCommand.

W poniższym przykładzie pokazano, jak dodać KeyGesture do InputGestureCollectionRoutedCommand.

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);
Dim OpenCmdKeyGesture As New KeyGesture(Key.B, ModifierKeys.Control)

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture)

Powiązanie poleceń

CommandBinding kojarzy polecenie z procedurami obsługi zdarzeń, które implementują polecenie.

Klasa CommandBinding zawiera właściwość Command oraz zdarzenia PreviewExecuted, Executed, PreviewCanExecutei CanExecute.

Command to polecenie, z którym jest skojarzona CommandBinding. Programy obsługi zdarzeń dołączone do PreviewExecuted i zdarzeń Executed implementują logikę poleceń. Programy obsługi zdarzeń dołączone do PreviewCanExecute i zdarzenia CanExecute określają, czy polecenie może zostać wykonane w bieżącym obiekcie docelowym polecenia.

W poniższym przykładzie pokazano, jak utworzyć CommandBinding na Window głównej aplikacji. CommandBinding kojarzy polecenie Open z procedurami obsługi Executed i CanExecute.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);
' Creating CommandBinding and attaching an Executed and CanExecute handler
Dim OpenCmdBinding As New CommandBinding(ApplicationCommands.Open, AddressOf OpenCmdExecuted, AddressOf OpenCmdCanExecute)

Me.CommandBindings.Add(OpenCmdBinding)

Następnie zostaną utworzone ExecutedRoutedEventHandler i CanExecuteRoutedEventHandler. ExecutedRoutedEventHandler otwiera MessageBox, który wyświetla komunikat informujący, że polecenie zostało wykonane. CanExecuteRoutedEventHandler ustawia właściwość CanExecute na wartość true.

void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
Private Sub OpenCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Dim command, targetobj As String
    command = CType(e.Command, RoutedCommand).Name
    targetobj = CType(sender, FrameworkElement).Name
    MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj)
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub

CommandBinding jest dołączany do określonego obiektu, takiego jak główny Window aplikacji lub kontrolki. Obiekt dołączony do CommandBinding definiuje zakres powiązania. Na przykład można uzyskać dostęp do CommandBinding dołączonego do przodka obiektu docelowego polecenia przez zdarzenie Executed, ale nie można uzyskać dostępu do CommandBinding dołączonego do elementu podrzędnego obiektu docelowego polecenia. Jest to bezpośrednia konsekwencja sposobu, w jaki RoutedEvent tuneluje i tworzy bąbelki od obiektu, który zgłasza zdarzenie.

W niektórych sytuacjach CommandBinding jest przypisany do samego obiektu docelowego polecenia, jak w przypadku klasy TextBox oraz poleceń Cut, Copyi Paste. Dość często jednak łatwiej jest dołączyć CommandBinding do przodka celu polecenia, takiego jak główny Window lub obiekt Aplikacji, zwłaszcza jeśli ten sam CommandBinding można zastosować do wielu celów poleceń. Są to decyzje projektowe, które należy wziąć pod uwagę podczas tworzenia waszej infrastruktury dowodzenia.

Cel polecenia

Elementem docelowym polecenia jest element, na którym jest wykonywane polecenie. Jeśli chodzi o RoutedCommand, celem polecenia jest element, od którego rozpoczyna się routing Executed i CanExecute. Jak wspomniano wcześniej, w WPF właściwość CommandTarget na ICommandSource ma zastosowanie tylko wtedy, gdy ICommand jest RoutedCommand. Jeśli CommandTarget jest ustawiona na ICommandSource, a odpowiednie polecenie nie jest RoutedCommand, obiekt docelowy polecenia jest ignorowany.

Źródło polecenia może jawnie ustawić docelowy obiekt polecenia. Jeśli element docelowy polecenia nie jest zdefiniowany, element z fokusem klawiatury będzie używany jako element docelowy polecenia. Jedną z zalet używania elementu z fokusem klawiatury jako obiektu docelowego polecenia jest to, że umożliwia deweloperowi aplikacji używanie tego samego źródła poleceń do wywoływania polecenia na wielu miejscach docelowych bez konieczności śledzenia celu polecenia. Jeśli na przykład wywołuje polecenie wklej w aplikacji, która ma kontrolkę i kontrolkę , obiektem docelowym może być lub w zależności od tego, która kontrolka ma fokus klawiatury.

W poniższym przykładzie pokazano, jak jawnie ustawić element docelowy polecenia w znaczników i w kodzie.

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()

' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)

' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste

The CommandManager

CommandManager obsługuje wiele funkcji związanych z poleceniami. Udostępnia zestaw statycznych metod do dodawania i usuwania obsług zdarzeń PreviewExecuted, Executed, PreviewCanExecutei CanExecute do i z określonego elementu. Zapewnia metodę rejestrowania CommandBinding i InputBinding obiektów w określonej klasie. CommandManager umożliwia także, za pośrednictwem zdarzenia RequerySuggested, powiadomienie polecenia, kiedy powinno zgłosić zdarzenie CanExecuteChanged.

Metoda InvalidateRequerySuggested wymusza na CommandManager wywołanie zdarzenia RequerySuggested. Jest to przydatne w przypadku warunków, które powinny wyłączyć/włączyć polecenie, ale nie są warunkami, o których CommandManager jest świadomy.

Biblioteka poleceń

WPF udostępnia zestaw wstępnie zdefiniowanych poleceń. Biblioteka poleceń składa się z następujących klas: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommandsi ComponentCommands. Te klasy udostępniają polecenia, takie jak Cut, BrowseBack i BrowseForward, Play, Stopi Pause.

Wiele z tych poleceń obejmuje zestaw domyślnych powiązań wejściowych. Jeśli na przykład określisz, że aplikacja obsługuje polecenie kopiowania, automatycznie uzyskasz powiązanie klawiatury "CTRL+C" Również otrzymasz powiązania dla innych urządzeń wejściowych, takich jak gesty pióra komputera tabletu i informacje o mowie.

Gdy odwołujesz się do poleceń w różnych bibliotekach poleceń przy użyciu języka XAML, zazwyczaj można pominąć nazwę klasy biblioteki, która udostępnia statyczną właściwość polecenia. Ogólnie rzecz biorąc, nazwy poleceń są jednoznaczne jako ciągi, a typy, do których należą, istnieją w celu zapewnienia logicznego grupowania poleceń, ale nie są niezbędne do rozróżnienia. Można na przykład określić Command="Cut" zamiast bardziej rozbudowanego Command="ApplicationCommands.Cut". Jest to mechanizm pomocniczy, który jest wbudowany w procesor WPF XAML dla poleceń, a dokładniej - jest to zachowanie konwertera typów ICommand, do którego procesor WPF XAML odwołuje się w trakcie ładowania.

Tworzenie poleceń niestandardowych

Jeśli polecenia w klasach bibliotek poleceń nie spełniają Twoich potrzeb, możesz utworzyć własne polecenia. Istnieją dwa sposoby tworzenia polecenia niestandardowego. Pierwszym z nich jest rozpoczęcie od podstaw i zaimplementowanie interfejsu ICommand. Innym sposobem i bardziej typowym podejściem jest utworzenie RoutedCommand lub RoutedUICommand.

Aby zapoznać się z przykładem tworzenia niestandardowego RoutedCommand, zobacz Przykład tworzenia niestandardowego polecenia trasowanego.

Zobacz też