Sdílet prostřednictvím


Přehled příkazů

Commanding je vstupní mechanismus ve Windows Presentation Foundation (WPF), který poskytuje zpracování vstupu na více sémantické úrovni než vstup zařízení. Příklady příkazů jsou operace Kopírovat, Vyjmouta Vložit, které se nacházejí v mnoha aplikacích.

Tento přehled definuje, které příkazy jsou ve WPF, které třídy jsou součástí příkazového modelu a jak používat a vytvářet příkazy v aplikacích.

Toto téma obsahuje následující části:

Co jsou příkazy

Příkazy mají několik účelů. Prvním účelem je oddělit sémantiku a objekt, který vyvolá příkaz z logiky, která příkaz spustí. To umožňuje, aby více a různorodých zdrojů vyvolalo stejnou logiku příkazu a umožnilo přizpůsobení logiky příkazů pro různé cíle. Například operace úprav kopírovat, Vyjmouta Vložit, které jsou nalezeny v mnoha aplikacích, lze vyvolat pomocí různých uživatelských akcí, pokud jsou implementovány pomocí příkazů. Aplikace může uživateli umožnit vyjmout vybrané objekty nebo text buď kliknutím na tlačítko, výběrem položky v nabídce nebo pomocí kombinace kláves, například CTRL+X. Pomocí příkazů můžete svázat každý typ akce uživatele se stejnou logikou.

Dalším účelem příkazů je indikovat, jestli je akce k dispozici. Chcete-li pokračovat v příkladu oříznutí objektu nebo textu, má akce smysl pouze v případě, že je něco vybráno. Pokud se uživatel pokusí výstřihnout objekt nebo text, aniž by bylo něco vybráno, nic se nestane. Pokud to chcete uživateli označit, mnoho aplikací zakáže tlačítka a položky nabídky, aby uživatel věděl, jestli je možné provést akci. Příkaz může indikovat, jestli je akce možná implementací CanExecute metody. Tlačítko se může přihlásit k odběru události CanExecuteChanged a být zakázáno, pokud CanExecute vrátí false, nebo být povoleno, pokud CanExecute vrátí true.

Sémantika příkazu může být konzistentní napříč aplikacemi a třídami, ale logika akce je specifická pro konkrétní objekt, na který se pracuje. Kombinace kláves CTRL+X vyvolá příkaz Vyjmout v textových třídách, třídách obrázků a webových prohlížečích, ale skutečná logika pro provedení operace Vyjmutí je definována aplikací, která provádí vyjmutí. RoutedCommand umožňuje klientům implementovat logiku. Textový objekt může vyjmout vybraný text do schránky, zatímco objekt obrázku může vyjmout vybraný obrázek. Když aplikace zpracuje Executed událost, má přístup k cíli příkazu a může provést odpovídající akci v závislosti na typu cíle.

Příklad jednoduchého příkazu ve WPF

Nejjednodušší způsob, jak použít příkaz ve WPF, je použít předdefinované RoutedCommand z jedné z tříd knihovny příkazů; použijte ovládací prvek, který má nativní podporu pro zpracování příkazu; a použijte ovládací prvek, který má nativní podporu pro vyvolání příkazu. Příkaz Paste je jedním z předdefinovaných příkazů ve třídě ApplicationCommands. Ovládací prvek TextBox má integrovanou logiku pro zpracování příkazu Paste. A třída MenuItem má nativní podporu pro vyvolání příkazů.

Následující příklad ukazuje, jak nastavit MenuItem tak, aby po kliknutí provedlo příkaz Paste na TextBox, za předpokladu, že TextBox má aktivní fokus klávesnice.

<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

Čtyři hlavní koncepty v commandingu WPF

Směrovaný model příkazů ve WPF je možné rozdělit do čtyř hlavních konceptů: příkaz, zdroj příkazů, cíl příkazu a vazba příkazu:

  • Příkaz je akce, která se má provést.

  • Zdroj příkazu je objekt, který příkaz vyvolá.

  • Cíl příkazu je objekt, na který se příkaz spouští.

  • Vazba příkazu je objekt, který mapuje logiku příkazu na tento příkaz.

V předchozím příkladu je příkaz Paste příkazem, MenuItem je zdrojem příkazů, TextBox je cíl příkazu a vazba příkazu je zadána ovládacím prvek TextBox. Stojí za zmínku, že není tomu vždy tak, že CommandBinding je dodáván ovládáním, které je cílové třídě příkazu. Poměrně často je zapotřebí, aby CommandBinding vytvořil vývojář aplikace, nebo může být CommandBinding připojen k předchůdci cílového příkazu.

Příkazy

Příkazy ve WPF se vytvářejí implementací ICommand rozhraní. ICommand zveřejňuje dvě metody, Executea CanExecutea událost CanExecuteChanged. Execute provede akce přidružené k příkazu. CanExecute určuje, jestli se příkaz může spustit v aktuálním cíli příkazu. CanExecuteChanged je vyvolána, pokud správce příkazů, který centralizuje příkazové operace, zjistí změnu ve zdroji příkazů, která může zneplatnit příkaz, který byl vyvolán, ale dosud nebyl spuštěn pomocí vazby příkazu. Implementace WPF ICommand je třída RoutedCommand a je hlavním tématem tohoto přehledu.

Hlavními zdroji vstupu ve WPF jsou myš, klávesnice, rukopis a směrované příkazy. Více vstupů orientovaných na zařízení používá RoutedEvent k upozornění objektů na stránce aplikace, že došlo ke vstupní události. RoutedCommand se nijak neliší. Execute a CanExecute metody RoutedCommand neobsahují logiku aplikace pro příkaz, ale spíše vyvolávají směrované události, které tunelují a bublinují přes strom prvků, dokud nenarazí na objekt s CommandBinding. CommandBinding obsahuje obslužné rutiny pro tyto události a jedná se o obslužné rutiny, které provádějí příkaz. Další informace o směrování událostí ve WPF naleznete v tématu Přehled trasovaných událostí.

Metoda Execute na RoutedCommand vyvolá události PreviewExecuted a Executed na cíli příkazu. Metoda CanExecute na RoutedCommand vyvolá CanExecute a PreviewCanExecute události na cílovém objektu příkazu. Tyto události tunelují a bublinují stromem prvků, dokud nenarazí na objekt, který má CommandBinding pro daný příkaz.

WPF poskytuje sadu běžných směrovaných příkazů rozložených do několika tříd: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommandsa EditingCommands. Tyto třídy se skládají pouze z RoutedCommand objektů, a ne z logiky implementace příkazu. Logika implementace je zodpovědností objektu, na kterém se příkaz spouští.

Zdroje příkazů

Zdroj příkazu je objekt, který příkaz vyvolá. Příklady zdrojů příkazů jsou MenuItem, Buttona KeyGesture.

Zdroje příkazů ve WPF obecně implementují rozhraní ICommandSource.

ICommandSource zveřejňuje tři vlastnosti: Command, CommandTargeta CommandParameter:

Třídy WPF, které implementují ICommandSource jsou ButtonBase, MenuItem, Hyperlinka InputBinding. ButtonBase, MenuItema Hyperlink vyvolají příkaz, když na ně kliknete, a InputBinding vyvolá příkaz, když je proveden jeho přidružený InputGesture.

Následující příklad ukazuje, jak použít MenuItem v ContextMenu jako zdroj příkazů pro příkaz 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

Zdroj příkazů obvykle naslouchá události CanExecuteChanged. Tato událost informuje zdroj příkazů, že se mohla změnit schopnost příkazu spustit v aktuálním cíli příkazu. Zdroj příkazů se může dotazovat na aktuální stav RoutedCommand pomocí metody CanExecute. Zdroj příkazu se pak může zakázat, pokud příkaz nejde spustit. Příkladem je, že MenuItem změní barvu na šedou, když se příkaz nemůže spustit.

InputGesture lze použít jako zdroj příkazů. Dva typy vstupních gest ve WPF jsou KeyGesture a MouseGesture. KeyGesture si můžete představit jako klávesovou zkratku, například CTRL+C. KeyGesture se skládá z Key a sady ModifierKeys. MouseGesture se skládá z MouseAction a volitelné sady ModifierKeys.

Aby InputGesture fungovala jako zdroj příkazů, musí být přidružena k příkazu. Existuje několik způsobů, jak toho dosáhnout. Jedním ze způsobů je použít InputBinding.

Následující příklad ukazuje, jak vytvořit KeyBinding mezi 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)

Dalším způsobem, jak přidružit InputGesture k RoutedCommand, je přidat InputGesture do InputGestureCollection na RoutedCommand.

Následující příklad ukazuje, jak přidat KeyGesture ke InputGestureCollection u RoutedCommand.

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)

CommandBinding

CommandBinding přidruží příkaz k obslužným rutinám událostí, které tento příkaz implementují.

Třída CommandBinding obsahuje vlastnost Command a události PreviewExecuted, Executed, PreviewCanExecutea CanExecute.

Command je příkaz, ke kterému je přidružená CommandBinding. Obslužné rutiny událostí, které jsou připojené k PreviewExecuted a Executed události implementují logiku příkazu. Obslužné procedury událostí připojené k událostem PreviewCanExecute a CanExecute určují, zda příkaz může být spuštěn na aktuálním cílovém objektu příkazu.

Následující příklad ukazuje, jak vytvořit CommandBinding v kořenovém Window aplikace. CommandBinding přiřadí příkaz Open k obslužným rutinám Executed a 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)

Dále se vytvoří ExecutedRoutedEventHandler a CanExecuteRoutedEventHandler. ExecutedRoutedEventHandler otevře MessageBox, ve kterém se zobrazí řetězec s informací, že byl příkaz proveden. CanExecuteRoutedEventHandler nastaví vlastnost CanExecute na 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 je připojen k určitému objektu, například ke kořenovému Window aplikace nebo ovládacímu prvku. Objekt, ke kterému je CommandBinding připojen, určuje rozsah vazby. Například CommandBinding připojené k předku cíle příkazu může být dosaženo událostí Executed, ale CommandBinding připojené k potomku cíle příkazu nelze dosáhnout. To je přímý důsledek způsobu, jakým RoutedEvent tuneluje a bubliny z objektu, který vyvolá událost.

V některých situacích je CommandBinding připojen k samotnému cíli příkazu, jako je například třída TextBox a Cut, Copya příkazy Paste. Docela často je ale vhodnější připojit CommandBinding k nadřazené části cíle příkazu, jako je hlavní Window nebo objekt aplikace, zejména pokud stejný CommandBinding lze použít pro více cílů příkazů. Jedná se o rozhodnutí o návrhu, která byste měli zvážit při vytváření infrastruktury příkazů.

Cíl příkazu

Cíl příkazu je prvek, na kterém se příkaz spustí. Pokud jde o RoutedCommand, cíl příkazu je prvek, na kterém začíná směrování Executed a CanExecute. Jak jsme uvedli dříve, ve WPF vlastnost CommandTarget pro ICommandSource je použitelná pouze v případě, že ICommand je RoutedCommand. Pokud je CommandTarget nastaven na ICommandSource a odpovídající příkaz není RoutedCommand, cíl příkazu se ignoruje.

Zdroj příkazů může explicitně nastavit cíl příkazu. Pokud cíl příkazu není definovaný, použije se jako cíl příkazu prvek s fokusem klávesnice. Jednou z výhod použití elementu s fokusem klávesnice jako cíl příkazu je, že vývojář aplikace může použít stejný zdroj příkazů k vyvolání příkazu na více cílech, aniž by musel sledovat cíl příkazu. Pokud například MenuItem vyvolá příkaz Vložit v aplikaci, která má ovládací prvek TextBox a ovládací prvek PasswordBox, může být cílem TextBox nebo PasswordBox v závislosti na tom, který ovládací prvek má fokus klávesnice.

Následující příklad ukazuje, jak explicitně nastavit cíl příkazu v označení a v kódu na pozadí.

<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

Správce příkazů

CommandManager obsluhuje řadu funkcí souvisejících s příkazy. Poskytuje sadu statických metod pro přidávání a odebírání PreviewExecuted, Executed, PreviewCanExecutea CanExecute správců událostí k a od konkrétního prvku. Poskytuje způsob registrace CommandBinding a InputBinding objektů do konkrétní třídy. CommandManager také poskytuje prostředky prostřednictvím události RequerySuggested, aby příkaz oznámil, kdy má vyvolat událost CanExecuteChanged.

Metoda InvalidateRequerySuggested přinutí CommandManager, aby vyvolal událost RequerySuggested. To je užitečné pro takové podmínky, které by měly deaktivovat nebo aktivovat příkaz, ale nejsou podmínky, o kterých \CommandManager neví.

Knihovna příkazů

WPF poskytuje sadu předdefinovaných příkazů. Knihovna příkazů se skládá z následujících tříd: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommandsa ComponentCommands. Tyto třídy poskytují příkazy, jako jsou Cut, BrowseBack a BrowseForward, Play, Stopa Pause.

Mnoho z těchto příkazů obsahuje sadu výchozích vstupních vazeb. Pokud například určíte, že aplikace zpracovává příkaz pro kopírování, automaticky získáte vazbu klávesnice CTRL+C. Získáte také vazby pro jiná vstupní zařízení, jako jsou gesta pera tabletu a informace o řeči.

Když odkazujete na příkazy v různých knihovnách příkazů pomocí XAML, můžete obvykle vynechat název třídy knihovny, která zveřejňuje statickou vlastnost příkazu. Obecně platí, že názvy příkazů jsou jednoznačné jako řetězce a vlastnící typy existují, aby poskytovaly logické seskupení příkazů, ale nejsou nezbytné pro nejednoznačnost. Můžete například zadat Command="Cut" místo více podrobných Command="ApplicationCommands.Cut". Jedná se o pohodlný mechanismus integrovaný v procesoru WPF XAML pro zpracování příkazů (přesněji řečeno, jedná se o chování převodníku typu ICommand, na který procesor WPF XAML odkazuje při načítání).

Vytváření vlastních příkazů

Pokud příkazy v třídách knihovny příkazů nevyhovují vašim potřebám, můžete vytvořit vlastní příkazy. Vlastní příkaz můžete vytvořit dvěma způsoby. První je začít od základů a implementovat ICommand rozhraní. Druhým způsobem a častějším přístupem je vytvoření RoutedCommand nebo RoutedUICommand.

Příklad vytvoření vlastního RoutedCommandnajdete v sekci Ukázka vytvoření vlastního RoutedCommand.

Viz také