Dela via


Kommandon i Windows-appar med StandardUICommand, XamlUICommand och ICommand

I det här avsnittet beskriver vi kommandon i Windows-program. Mer specifikt diskuterar vi hur du kan använda klasserna XamlUICommand och StandardUICommand (tillsammans med ICommand-gränssnittet) för att dela och hantera kommandon mellan olika kontrolltyper, oavsett vilken enhet och indatatyp som används.

Ett diagram som representerar en vanlig användning för ett delat kommando: flera gränssnittsytor med kommandot

Dela kommandon mellan olika kontroller, oavsett enhet och indatatyp

Viktiga API:er

Översikt

Kommandon kan anropas direkt via gränssnittsinteraktioner som att klicka på en knapp eller välja ett objekt från en snabbmeny. De kan också anropas indirekt via en indataenhet, till exempel en tangentbordsaccelerator, gest, taligenkänning eller ett automatiserings-/hjälpmedelsverktyg. När kommandot har anropats kan det hanteras av en kontroll (textnavigering i en redigeringskontroll), ett fönster (bakåtnavigering) eller programmet (avsluta).

Kommandon kan fungera i en specifik kontext i din app, till exempel att ta bort text eller ångra en åtgärd, eller att de kan vara kontextfria, till exempel att stänga av ljud eller justera ljusstyrkan.

Följande bild visar två kommandogränssnitt (en kommandorad och en flytande kontextuell CommandBarFlyout) som delar några av samma kommandon.

Expanderat kommandofält
Kommandofält

Snabbmeny i Microsoft Photos-galleriet
Snabbmeny i Microsoft Photos-galleriet

Kommandointeraktioner

På grund av de olika enheter, indatatyper och gränssnittsytor som kan påverka hur ett kommando anropas rekommenderar vi att du exponerar dina kommandon via så många kommandoytor som möjligt. Dessa kan innehålla en kombination av Svep, Menyrad, Kommandorad, CommandBarFlyout och traditionell snabbmeny.

För kritiska kommandon använder du indataspecifika acceleratorer. Med indataacceleratorer kan en användare utföra åtgärder snabbare beroende på vilken indataenhet de använder.

Här följer några vanliga indataacceleratorer för olika indatatyper:

  • Pekare – mus- och pennknappar
  • Tangentbord – Genvägar (åtkomstnycklar och acceleratornycklar)
  • Touch - Svep
  • Touch – Hämta för att uppdatera data

Du måste överväga indatatypen och användarupplevelsen för att göra programmets funktioner universellt tillgängliga. Samlingar (särskilt användarredigerbara) innehåller till exempel vanligtvis en mängd olika kommandon som utförs helt annorlunda beroende på indataenheten.

I följande tabell visas några typiska samlingskommandon och sätt att exponera dessa kommandon.

Command Indataagnostisk musacceleratorfunktion Tangentbordsaccelerator Pekaccelerator
Ta bort objekt Kontekstmeny Hovringsknapp DEL-nyckel Svep för att ta bort
Flagga objekt Kontekstmeny Hovringsknapp Ctrl+Skift+G Svep till flagga
Uppdatera data Kontekstmeny N/A F5-nyckel Dra för att uppdatera
Favorisera ett objekt Kontekstmeny Hovringsknapp F, Ctrl+S Svep till favorit

Ange alltid en snabbmeny Vi rekommenderar att du inkluderar alla relevanta kontextuella kommandon i en traditionell snabbmeny eller CommandBarFlyout, eftersom båda stöds för alla indatatyper. Om ett kommando till exempel endast exponeras under en hovringshändelse för pekare kan det inte användas på en enhet med endast pekfunktioner.

Kommandon i Windows-program

Det finns några sätt att dela och hantera kommandofunktioner i ett Windows-program. Du kan definiera händelsehanterare för standardinteraktioner, till exempel Klicka, i kod bakom (detta kan vara ganska ineffektivt, beroende på komplexiteten i ditt användargränssnitt), du kan binda händelselyssnaren för standardinteraktioner till en delad hanterare, eller så kan du binda kontrollens kommandoegenskap till en ICommand-implementering som beskriver kommandologiken.

För att ge omfattande och omfattande användarupplevelser över kommandoytor effektivt och med minimal kodduplicering rekommenderar vi att du använder de kommandobindningsfunktioner som beskrivs i det här avsnittet (för standardhändelsehantering kan du läsa de enskilda händelseavsnitten).

Om du vill binda en kontroll till en delad kommandoresurs kan du implementera ICommand-gränssnitten själv, eller så kan du skapa kommandot från antingen basklassen XamlUICommand eller något av plattformskommandona som definieras av den härledda klassen StandardUICommand .

  • Med ICommand-gränssnittet (Windows.UI.Xaml.Input.ICommand eller System.Windows.Input.ICommand) kan du skapa helt anpassade, återanvändbara kommandon i hela appen.
  • XamlUICommand tillhandahåller också den här funktionen men förenklar utvecklingen genom att exponera en uppsättning inbyggda kommandoegenskaper som kommandobeteende, kortkommandon (åtkomstnyckel och acceleratornyckel), ikon, etikett och beskrivning.
  • StandardUICommand förenklar saker ytterligare genom att låta dig välja mellan en uppsättning standardplattformskommandon med fördefinierade egenskaper.

Viktigt!

I UWP-program är kommandon implementeringar av antingen gränssnittet Windows.UI.Xaml.Input.ICommand (C++) eller System.Windows.Input.ICommand (C#) beroende på ditt valda språkramverk.

Kommandoupplevelser med klassen StandardUICommand

Härledd från XamlUICommand (härledd från Windows.UI.Xaml.Input.ICommand för C++ eller System.Windows.Input.ICommand för C#) exponerar klassen StandardUICommand en uppsättning standardplattformskommandon med fördefinierade egenskaper som ikon, tangentbordsaccelerator och beskrivning.

En StandardUICommand ger ett snabbt och konsekvent sätt att definiera vanliga kommandon som Save eller Delete. Allt du behöver göra är att ange funktionerna execute och canExecute.

Example

StandardUICommand-exempel

StandardUICommandSample

Ladda ned koden för det här exemplet
UWP-kommandoexempel (StandardUICommand)

I det här exemplet visar vi hur du förbättrar en grundläggande ListView med kommandot Ta bort objekt som implementerats via klassen StandardUICommand , samtidigt som du optimerar användarupplevelsen för en mängd olika indatatyper med hjälp av en menyrad, svepkontroll , hovringsknappar och snabbmeny.

Anmärkning

Det här exemplet kräver NuGet-paketet Microsoft.UI.Xaml.Controls, en del av WinUI 2.

Xaml:

Exempelgränssnittet innehåller en ListView med fem objekt. "Kommandot StandardUICommand för att ta bort är kopplat till en MenuBarItem, en SwipeItem, en AppBarButton, och en ContextFlyout-meny."

<Page
    x:Class="StandardUICommandSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:StandardUICommandSample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:muxcontrols="using:Microsoft.UI.Xaml.Controls"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <Style x:Key="HorizontalSwipe" 
               TargetType="ListViewItem" 
               BasedOn="{StaticResource ListViewItemRevealStyle}">
            <Setter Property="Height" Value="60"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
    </Page.Resources>

    <Grid Loaded="ControlExample_Loaded">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" 
                    Padding="10" 
                    BorderThickness="0,0,0,1" 
                    BorderBrush="LightBlue"
                    Background="AliceBlue">
            <TextBlock Style="{StaticResource HeaderTextBlockStyle}">
                StandardUICommand sample
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,10">
                This sample shows how to use the StandardUICommand class to 
                share a platform command and consistent user experiences 
                across various controls.
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,0">
                Specifically, we define a standard delete command and add it 
                to a variety of command surfaces, all of which share a common 
                icon, label, keyboard accelerator, and description.
            </TextBlock>
        </StackPanel>

        <muxcontrols:MenuBar Grid.Row="1" Padding="10">
            <muxcontrols:MenuBarItem Title="File">
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Edit">
                <MenuFlyoutItem x:Name="DeleteFlyoutItem"/>
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Help">
            </muxcontrols:MenuBarItem>
        </muxcontrols:MenuBar>

        <ListView x:Name="ListViewRight" Grid.Row="2" 
                  Loaded="ListView_Loaded" 
                  IsItemClickEnabled="True" 
                  SelectionMode="Single" 
                  SelectionChanged="ListView_SelectionChanged" 
                  ItemContainerStyle="{StaticResource HorizontalSwipe}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:ListItemData">
                    <UserControl PointerEntered="ListViewSwipeContainer_PointerEntered" 
                                 PointerExited="ListViewSwipeContainer_PointerExited">
                        <UserControl.ContextFlyout>
                            <MenuFlyout>
                                <MenuFlyoutItem 
                                    Command="{x:Bind Command}" 
                                    CommandParameter="{x:Bind Text}" />
                            </MenuFlyout>
                        </UserControl.ContextFlyout>
                        <Grid AutomationProperties.Name="{x:Bind Text}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="HoveringStates">
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>
                                            <Setter Target="HoverButton.Visibility" 
                                                    Value="Visible" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <SwipeControl x:Name="ListViewSwipeContainer" >
                                <SwipeControl.RightItems>
                                    <SwipeItems Mode="Execute">
                                        <SwipeItem x:Name="DeleteSwipeItem" 
                                                   Background="Red" 
                                                   Command="{x:Bind Command}" 
                                                   CommandParameter="{x:Bind Text}"/>
                                    </SwipeItems>
                                </SwipeControl.RightItems>
                                <Grid VerticalAlignment="Center">
                                    <TextBlock Text="{x:Bind Text}" 
                                               Margin="10" 
                                               FontSize="18" 
                                               HorizontalAlignment="Left" 
                                               VerticalAlignment="Center"/>
                                    <AppBarButton x:Name="HoverButton" 
                                                  IsTabStop="False" 
                                                  HorizontalAlignment="Right" 
                                                  Visibility="Collapsed" 
                                                  Command="{x:Bind Command}" 
                                                  CommandParameter="{x:Bind Text}"/>
                                </Grid>
                            </SwipeControl>
                        </Grid>
                    </UserControl>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

Kod bakom

  1. Först definierar vi en ListItemData klass som innehåller en textsträng och ICommand för varje ListViewItem i vår ListView.
public class ListItemData
{
    public String Text { get; set; }
    public ICommand Command { get; set; }
}
  1. I klassen MainPage definierar vi en samling ListItemData objekt för DataTemplate för ListViewItemTemplate. Sedan fyller vi i den med en första samling med fem objekt (med text och tillhörande StandardUICommand Delete).
/// <summary>
/// ListView item collection.
/// </summary>
ObservableCollection<ListItemData> collection = 
    new ObservableCollection<ListItemData>();

/// <summary>
/// Handler for the layout Grid control load event.
/// </summary>
/// <param name="sender">Source of the control loaded event</param>
/// <param name="e">Event args for the loaded event</param>
private void ControlExample_Loaded(object sender, RoutedEventArgs e)
{
    // Create the standard Delete command.
    var deleteCommand = new StandardUICommand(StandardUICommandKind.Delete);
    deleteCommand.ExecuteRequested += DeleteCommand_ExecuteRequested;

    DeleteFlyoutItem.Command = deleteCommand;

    for (var i = 0; i < 5; i++)
    {
        collection.Add(
            new ListItemData {
                Text = "List item " + i.ToString(),
                Command = deleteCommand });
    }
}

/// <summary>
/// Handler for the ListView control load event.
/// </summary>
/// <param name="sender">Source of the control loaded event</param>
/// <param name="e">Event args for the loaded event</param>
private void ListView_Loaded(object sender, RoutedEventArgs e)
{
    var listView = (ListView)sender;
    // Populate the ListView with the item collection.
    listView.ItemsSource = collection;
}
  1. Därefter definierar vi ICommand ExecuteRequested-hanteraren där vi implementerar kommandot ta bort objekt.
/// <summary>
/// Handler for the Delete command.
/// </summary>
/// <param name="sender">Source of the command event</param>
/// <param name="e">Event args for the command event</param>
private void DeleteCommand_ExecuteRequested(
    XamlUICommand sender, ExecuteRequestedEventArgs args)
{
    // If possible, remove specified item from collection.
    if (args.Parameter != null)
    {
        foreach (var i in collection)
        {
            if (i.Text == (args.Parameter as string))
            {
                collection.Remove(i);
                return;
            }
        }
    }
    if (ListViewRight.SelectedIndex != -1)
    {
        collection.RemoveAt(ListViewRight.SelectedIndex);
    }
}
  1. Slutligen definierar vi hanterare för olika ListView-händelser, inklusive PointerEntered-, PointerExited- och SelectionChanged-händelser . Pekarens händelsehanterare används för att visa eller dölja knappen Ta bort för varje objekt.
/// <summary>
/// Handler for the ListView selection changed event.
/// </summary>
/// <param name="sender">Source of the selection changed event</param>
/// <param name="e">Event args for the selection changed event</param>
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (ListViewRight.SelectedIndex != -1)
    {
        var item = collection[ListViewRight.SelectedIndex];
    }
}

/// <summary>
/// Handler for the pointer entered event.
/// Displays the delete item "hover" buttons.
/// </summary>
/// <param name="sender">Source of the pointer entered event</param>
/// <param name="e">Event args for the pointer entered event</param>
private void ListViewSwipeContainer_PointerEntered(
    object sender, PointerRoutedEventArgs e)
{
    if (e.Pointer.PointerDeviceType == 
        Windows.Devices.Input.PointerDeviceType.Mouse || 
        e.Pointer.PointerDeviceType == 
        Windows.Devices.Input.PointerDeviceType.Pen)
    {
        VisualStateManager.GoToState(
            sender as Control, "HoverButtonsShown", true);
    }
}

/// <summary>
/// Handler for the pointer exited event.
/// Hides the delete item "hover" buttons.
/// </summary>
/// <param name="sender">Source of the pointer exited event</param>
/// <param name="e">Event args for the pointer exited event</param>

private void ListViewSwipeContainer_PointerExited(
    object sender, PointerRoutedEventArgs e)
{
    VisualStateManager.GoToState(
        sender as Control, "HoverButtonsHidden", true);
}

Kommandoupplevelser med hjälp av klassen XamlUICommand

Om du behöver skapa ett kommando som inte definieras av klassen StandardUICommand , eller om du vill ha mer kontroll över kommandoutseendet, kommer klassen XamlUICommand från ICommand-gränssnittet och lägger till olika gränssnittsegenskaper (till exempel en ikon, etikett, beskrivning och kortkommandon), metoder och händelser för att snabbt definiera användargränssnittet och beteendet för ett anpassat kommando.

Med XamlUICommand kan du ange användargränssnittet via kontrollbindningen, till exempel en ikon, etikett, beskrivning och kortkommandon (både en åtkomstnyckel och en tangentbordsaccelerator) utan att ange de enskilda egenskaperna.

Example

XamlUICommand-exempel

XamlUICommandSample

Ladda ned koden för det här exemplet
UWP-kommandoexempel (XamlUICommand)

Det här exemplet delar funktionen Ta bort i föregående StandardUICommand-exempel , men visar hur klassen XamlUICommand låter dig definiera ett anpassat borttagningskommando med din egen teckensnittsikon, etikett, tangentbordsaccelerator och beskrivning. Precis som i Exemplet StandardUICommand förbättrar vi en grundläggande ListView med kommandot Ta bort objekt som implementerats via klassen XamlUICommand , samtidigt som användarupplevelsen optimeras för en mängd olika indatatyper med hjälp av en menyrad, svepkontroll , hovringsknappar och snabbmeny.

Många plattformskontroller använder XamlUICommand-egenskaperna under täcket, precis som vårt StandardUICommand-exempel i föregående avsnitt.

Anmärkning

Det här exemplet kräver NuGet-paketet Microsoft.UI.Xaml.Controls, en del av WinUI 2.

Xaml:

Exempelgränssnittet innehåller en ListView med fem objekt. Den anpassade XamlUICommand CustomXamlUICommand är bunden till en MenuBarItem, en SwipeItem, en AppBarButton- och ContextFlyout-meny.

<Page
    x:Class="XamlUICommand_Sample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:XamlUICommand_Sample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:muxcontrols="using:Microsoft.UI.Xaml.Controls"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <XamlUICommand x:Name="CustomXamlUICommand" 
                       ExecuteRequested="DeleteCommand_ExecuteRequested"
                       Description="Custom XamlUICommand" 
                       Label="Custom XamlUICommand">
            <XamlUICommand.IconSource>
                <FontIconSource FontFamily="Wingdings" Glyph="&#x4D;"/>
            </XamlUICommand.IconSource>
            <XamlUICommand.KeyboardAccelerators>
                <KeyboardAccelerator Key="D" Modifiers="Control"/>
            </XamlUICommand.KeyboardAccelerators>
        </XamlUICommand>

        <Style x:Key="HorizontalSwipe" 
               TargetType="ListViewItem" 
               BasedOn="{StaticResource ListViewItemRevealStyle}">
            <Setter Property="Height" Value="70"/>
            <Setter Property="Padding" Value="0"/>
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
            <Setter Property="VerticalContentAlignment" Value="Stretch"/>
            <Setter Property="BorderThickness" Value="0"/>
        </Style>
        
    </Page.Resources>

    <Grid Loaded="ControlExample_Loaded" Name="MainGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        
        <StackPanel Grid.Row="0" 
                    Padding="10" 
                    BorderThickness="0,0,0,1" 
                    BorderBrush="LightBlue"
                    Background="AliceBlue">
            <TextBlock Style="{StaticResource HeaderTextBlockStyle}">
                XamlUICommand sample
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,10">
                This sample shows how to use the XamlUICommand class to 
                share a custom command with consistent user experiences 
                across various controls.
            </TextBlock>
            <TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Margin="0,0,0,0">
                Specifically, we define a custom delete command and add it 
                to a variety of command surfaces, all of which share a common 
                icon, label, keyboard accelerator, and description.
            </TextBlock>
        </StackPanel>

        <muxcontrols:MenuBar Grid.Row="1">
            <muxcontrols:MenuBarItem Title="File">
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Edit">
                <MenuFlyoutItem x:Name="DeleteFlyoutItem" 
                                Command="{StaticResource CustomXamlUICommand}"/>
            </muxcontrols:MenuBarItem>
            <muxcontrols:MenuBarItem Title="Help">
            </muxcontrols:MenuBarItem>
        </muxcontrols:MenuBar>

        <ListView x:Name="ListViewRight" Grid.Row="2" 
                  Loaded="ListView_Loaded" 
                  IsItemClickEnabled="True"
                  SelectionMode="Single" 
                  SelectionChanged="ListView_SelectionChanged" 
                  ItemContainerStyle="{StaticResource HorizontalSwipe}">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="local:ListItemData">
                    <UserControl PointerEntered="ListViewSwipeContainer_PointerEntered"
                                 PointerExited="ListViewSwipeContainer_PointerExited">
                        <UserControl.ContextFlyout>
                            <MenuFlyout>
                                <MenuFlyoutItem 
                                    Command="{x:Bind Command}" 
                                    CommandParameter="{x:Bind Text}" />
                            </MenuFlyout>
                        </UserControl.ContextFlyout>
                        <Grid AutomationProperties.Name="{x:Bind Text}">
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="HoveringStates">
                                    <VisualState x:Name="HoverButtonsHidden" />
                                    <VisualState x:Name="HoverButtonsShown">
                                        <VisualState.Setters>
                                            <Setter Target="HoverButton.Visibility" 
                                                    Value="Visible" />
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <SwipeControl x:Name="ListViewSwipeContainer">
                                <SwipeControl.RightItems>
                                    <SwipeItems Mode="Execute">
                                        <SwipeItem x:Name="DeleteSwipeItem"
                                                   Background="Red" 
                                                   Command="{x:Bind Command}" 
                                                   CommandParameter="{x:Bind Text}"/>
                                    </SwipeItems>
                                </SwipeControl.RightItems>
                                <Grid VerticalAlignment="Center">
                                    <TextBlock Text="{x:Bind Text}" 
                                               Margin="10" 
                                               FontSize="18" 
                                               HorizontalAlignment="Left"       
                                               VerticalAlignment="Center"/>
                                    <AppBarButton x:Name="HoverButton" 
                                                  IsTabStop="False" 
                                                  HorizontalAlignment="Right" 
                                                  Visibility="Collapsed" 
                                                  Command="{x:Bind Command}" 
                                                  CommandParameter="{x:Bind Text}"/>
                                </Grid>
                            </SwipeControl>
                        </Grid>
                    </UserControl>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

Bakomliggande kod

  1. Först definierar vi en ListItemData klass som innehåller en textsträng och ICommand för varje ListViewItem i vår ListView.
public class ListItemData
{
    public String Text { get; set; }
    public ICommand Command { get; set; }
}
  1. I klassen MainPage definierar vi en samling ListItemData objekt för DataTemplate för ListViewItemTemplate. Sedan fyller vi i den med en första samling med fem objekt (med text och tillhörande XamlUICommand).
ObservableCollection<ListItemData> collection = new ObservableCollection<ListItemData>();

private void ControlExample_Loaded(object sender, RoutedEventArgs e)
{
    for (var i = 0; i < 5; i++)
    {
        collection.Add(
           new ListItemData { Text = "List item " + i.ToString(), Command = CustomXamlUICommand });
    }
}

private void ListView_Loaded(object sender, RoutedEventArgs e)
{
    var listView = (ListView)sender;
    listView.ItemsSource = collection;
}
  1. Därefter definierar vi ICommand ExecuteRequested-hanteraren där vi implementerar kommandot ta bort objekt.
private void DeleteCommand_ExecuteRequested(
   XamlUICommand sender, ExecuteRequestedEventArgs args)
{
    if (args.Parameter != null)
    {
        foreach (var i in collection)
        {
            if (i.Text == (args.Parameter as string))
            {
                collection.Remove(i);
                return;
            }
        }
    }
    if (ListViewRight.SelectedIndex != -1)
    {
        collection.RemoveAt(ListViewRight.SelectedIndex);
    }
}
  1. Slutligen definierar vi hanterare för olika ListView-händelser, inklusive PointerEntered-, PointerExited- och SelectionChanged-händelser . Pekarens händelsehanterare används för att visa eller dölja knappen Ta bort för varje objekt.
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (ListViewRight.SelectedIndex != -1)
    {
        var item = collection[ListViewRight.SelectedIndex];
    }
}

private void ListViewSwipeContainer_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    if (e.Pointer.PointerDeviceType == 
        Windows.Devices.Input.PointerDeviceType.Mouse || 
        e.Pointer.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Pen)
    {
        VisualStateManager.GoToState(sender as Control, "HoverButtonsShown", true);
    }
}

private void ListViewSwipeContainer_PointerExited(object sender, PointerRoutedEventArgs e)
{
    VisualStateManager.GoToState(sender as Control, "HoverButtonsHidden", true);
}

Kommandoupplevelser med ICommand-gränssnittet

Standard-UWP-kontroller (knapp, lista, val, kalender, förutsägande text) utgör grunden för många vanliga kommandoupplevelser. En fullständig lista över kontrolltyper finns i Kontroller och mönster för Windows-appar.

Det mest grundläggande sättet att stödja en strukturerad kommandoupplevelse är att definiera en implementering av ICommand-gränssnittet (Windows.UI.Xaml.Input.ICommand för C++ eller System.Windows.Input.ICommand för C#). Den här ICommand-instansen kan sedan bindas till kontroller, till exempel knappar.

Anmärkning

I vissa fall kan det vara lika effektivt att binda en metod till click-händelsen och en egenskap till egenskapen IsEnabled.

Example

Exempel på kommandogränssnitt

ICommand-exempel

Ladda ned koden för det här exemplet
UWP-kommandoexempel (ICommand)

I det här grundläggande exemplet visar vi hur ett enda kommando kan anropas med ett knappklick, en tangentbordsaccelerator och roterar ett mushjul.

Vi använder två ListViews, en ifylld med fem objekt och den andra tom, och två knappar, en för att flytta objekt från ListView till vänster till ListView till höger och den andra för att flytta objekt från höger till vänster. Varje knapp är bunden till ett motsvarande kommando (ViewModel.MoveRightCommand respektive ViewModel.MoveLeftCommand) och aktiveras och inaktiveras automatiskt baserat på antalet objekt i deras associerade ListView.

Följande XAML-kod definierar användargränssnittet för vårt exempel.

<Page
    x:Class="UICommand1.View.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="using:UICommand1.ViewModel"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Page.Resources>
        <vm:OpacityConverter x:Key="opaque" />
    </Page.Resources>

    <Grid Name="ItemGrid"
          Background="AliceBlue"
          PointerWheelChanged="Page_PointerWheelChanged">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="2*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <ListView Grid.Column="0" VerticalAlignment="Center"
                  x:Name="CommandListView" 
                  ItemsSource="{x:Bind Path=ViewModel.ListItemLeft}" 
                  SelectionMode="None" IsItemClickEnabled="False" 
                  HorizontalAlignment="Right">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="vm:ListItemData">
                    <Grid VerticalAlignment="Center">
                        <AppBarButton Label="{x:Bind ListItemText}">
                            <AppBarButton.Icon>
                                <SymbolIcon Symbol="{x:Bind ListItemIcon}"/>
                            </AppBarButton.Icon>
                        </AppBarButton>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Grid Grid.Column="1" Margin="0,0,0,0"
              HorizontalAlignment="Center" 
              VerticalAlignment="Center">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="1">
                <FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" 
                          FontSize="40" Glyph="&#xE893;" 
                          Opacity="{x:Bind Path=ViewModel.ListItemLeft.Count, 
                                        Mode=OneWay, Converter={StaticResource opaque}}"/>
                <Button Name="MoveItemRightButton"
                        Margin="0,10,0,10" Width="120" HorizontalAlignment="Center"
                        Command="{x:Bind Path=ViewModel.MoveRightCommand}">
                    <Button.KeyboardAccelerators>
                        <KeyboardAccelerator 
                            Modifiers="Control" 
                            Key="Add" />
                    </Button.KeyboardAccelerators>
                    <StackPanel>
                        <SymbolIcon Symbol="Next"/>
                        <TextBlock>Move item right</TextBlock>
                    </StackPanel>
                </Button>
                <Button Name="MoveItemLeftButton" 
                            Margin="0,10,0,10" Width="120" HorizontalAlignment="Center"
                            Command="{x:Bind Path=ViewModel.MoveLeftCommand}">
                    <Button.KeyboardAccelerators>
                        <KeyboardAccelerator 
                            Modifiers="Control" 
                            Key="Subtract" />
                    </Button.KeyboardAccelerators>
                    <StackPanel>
                        <SymbolIcon Symbol="Previous"/>
                        <TextBlock>Move item left</TextBlock>
                    </StackPanel>
                </Button>
                <FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" 
                          FontSize="40" Glyph="&#xE892;"
                          Opacity="{x:Bind Path=ViewModel.ListItemRight.Count, 
                                        Mode=OneWay, Converter={StaticResource opaque}}"/>
            </StackPanel>
        </Grid>
        <ListView Grid.Column="2" 
                  x:Name="CommandListViewRight" 
                  VerticalAlignment="Center" 
                  IsItemClickEnabled="False" 
                  SelectionMode="None"
                  ItemsSource="{x:Bind Path=ViewModel.ListItemRight}" 
                  HorizontalAlignment="Left">
            <ListView.ItemTemplate>
                <DataTemplate x:DataType="vm:ListItemData">
                    <Grid VerticalAlignment="Center">
                        <AppBarButton Label="{x:Bind ListItemText}">
                            <AppBarButton.Icon>
                                <SymbolIcon Symbol="{x:Bind ListItemIcon}"/>
                            </AppBarButton.Icon>
                        </AppBarButton>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
</Page>

Här är koden bakom för föregående användargränssnitt.

I code-behind ansluter vi till vår vymodell som innehåller vår kommandokod. Dessutom definierar vi en hanterare för indata från mushjulet, som även ansluter vår kommandokod.

using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Controls;
using UICommand1.ViewModel;
using Windows.System;
using Windows.UI.Core;

namespace UICommand1.View
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        // Reference to our view model.
        public UICommand1ViewModel ViewModel { get; set; }

        // Initialize our view and view model.
        public MainPage()
        {
            this.InitializeComponent();
            ViewModel = new UICommand1ViewModel();
        }

        /// <summary>
        /// Handle mouse wheel input and assign our
        /// commands to appropriate direction of rotation.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Page_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
        {
            var props = e.GetCurrentPoint(sender as UIElement).Properties;

            // Require CTRL key and accept only vertical mouse wheel movement 
            // to eliminate accidental wheel input.
            if ((Window.Current.CoreWindow.GetKeyState(VirtualKey.Control) != 
                CoreVirtualKeyStates.None) && !props.IsHorizontalMouseWheel)
            {
                bool delta = props.MouseWheelDelta < 0 ? true : false;

                switch (delta)
                {
                    case true:
                        ViewModel.MoveRight();
                        break;
                    case false:
                        ViewModel.MoveLeft();
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

Här är koden från vår vymodell

Vår vymodell är den plats där vi definierar körningsinformationen för de två kommandona i vår app, fyller i en ListView och tillhandahåller en konverterare för opacitetsvärde för att dölja eller visa ytterligare användargränssnitt baserat på antalet objekt för varje ListView.

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;

namespace UICommand1.ViewModel
{
    /// <summary>
    /// UI properties for our list items.
    /// </summary>
    public class ListItemData
    {
        /// <summary>
        /// Gets and sets the list item content string.
        /// </summary>
        public string ListItemText { get; set; }
        /// <summary>
        /// Gets and sets the list item icon.
        /// </summary>
        public Symbol ListItemIcon { get; set; }
    }

    /// <summary>
    /// View Model that sets up a command to handle invoking the move item buttons.
    /// </summary>
    public class UICommand1ViewModel
    {
        /// <summary>
        /// The command to invoke when the Move item left button is pressed.
        /// </summary>
        public RelayCommand MoveLeftCommand { get; private set; }

        /// <summary>
        /// The command to invoke when the Move item right button is pressed.
        /// </summary>
        public RelayCommand MoveRightCommand { get; private set; }

        // Item collections
        public ObservableCollection<ListItemData> ListItemLeft { get; } = 
           new ObservableCollection<ListItemData>();
        public ObservableCollection<ListItemData> ListItemRight { get; } = 
           new ObservableCollection<ListItemData>();

        public ListItemData listItem;

        /// <summary>
        /// Sets up a command to handle invoking the move item buttons.
        /// </summary>
        public UICommand1ViewModel()
        {
            MoveLeftCommand = 
               new RelayCommand(new Action(MoveLeft), CanExecuteMoveLeftCommand);
            MoveRightCommand = 
               new RelayCommand(new Action(MoveRight), CanExecuteMoveRightCommand);

            LoadItems();
        }

        /// <summary>
        ///  Populate our list of items.
        /// </summary>
        public void LoadItems()
        {
            for (var x = 0; x <= 4; x++)
            {
                listItem = new ListItemData();
                listItem.ListItemText = "Item " + (ListItemLeft.Count + 1).ToString();
                listItem.ListItemIcon = Symbol.Emoji;
                ListItemLeft.Add(listItem);
            }
        }

        /// <summary>
        /// Move left command valid when items present in the list on right.
        /// </summary>
        /// <returns>True, if count is greater than 0.</returns>
        private bool CanExecuteMoveLeftCommand()
        {
            return ListItemRight.Count > 0;
        }

        /// <summary>
        /// Move right command valid when items present in the list on left.
        /// </summary>
        /// <returns>True, if count is greater than 0.</returns>
        private bool CanExecuteMoveRightCommand()
        {
            return ListItemLeft.Count > 0;
        }

        /// <summary>
        /// The command implementation to execute when the Move item right button is pressed.
        /// </summary>
        public void MoveRight()
        {
            if (ListItemLeft.Count > 0)
            {
                listItem = new ListItemData();
                ListItemRight.Add(listItem);
                listItem.ListItemText = "Item " + ListItemRight.Count.ToString();
                listItem.ListItemIcon = Symbol.Emoji;
                ListItemLeft.RemoveAt(ListItemLeft.Count - 1);
                MoveRightCommand.RaiseCanExecuteChanged();
                MoveLeftCommand.RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// The command implementation to execute when the Move item left button is pressed.
        /// </summary>
        public void MoveLeft()
        {
            if (ListItemRight.Count > 0)
            {
                listItem = new ListItemData();
                ListItemLeft.Add(listItem);
                listItem.ListItemText = "Item " + ListItemLeft.Count.ToString();
                listItem.ListItemIcon = Symbol.Emoji;
                ListItemRight.RemoveAt(ListItemRight.Count - 1);
                MoveRightCommand.RaiseCanExecuteChanged();
                MoveLeftCommand.RaiseCanExecuteChanged();
            }
        }

        /// <summary>
        /// Views subscribe to this event to get notified of property updates.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notify subscribers of updates to the named property
        /// </summary>
        /// <param name="propertyName">The full, case-sensitive, name of a property.</param>
        protected void NotifyPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                PropertyChangedEventArgs args = new PropertyChangedEventArgs(propertyName);
                handler(this, args);
            }
        }
    }

    /// <summary>
    /// Convert a collection count to an opacity value of 0.0 or 1.0.
    /// </summary>
    public class OpacityConverter : IValueConverter
    {
        /// <summary>
        /// Converts a collection count to an opacity value of 0.0 or 1.0.
        /// </summary>
        /// <param name="value">The count passed in</param>
        /// <param name="targetType">Ignored.</param>
        /// <param name="parameter">Ignored</param>
        /// <param name="language">Ignored</param>
        /// <returns>1.0 if count > 0, otherwise returns 0.0</returns>
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return ((int)value > 0 ? 1.0 : 0.0);
        }

        /// <summary>
        /// Not used, converter is not intended for two-way binding. 
        /// </summary>
        /// <param name="value">Ignored</param>
        /// <param name="targetType">Ignored</param>
        /// <param name="parameter">Ignored</param>
        /// <param name="language">Ignored</param>
        /// <returns></returns>
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
}

Slutligen, här är vår implementering av ICommand-gränssnittet

Här definierar vi ett kommando som implementerar ICommand-gränssnittet och helt enkelt vidarebefordrar dess funktioner till andra objekt.

using System;
using System.Windows.Input;

namespace UICommand1
{
    /// <summary>
    /// A command whose sole purpose is to relay its functionality 
    /// to other objects by invoking delegates. 
    /// The default return value for the CanExecute method is 'true'.
    /// <see cref="RaiseCanExecuteChanged"/> needs to be called whenever
    /// <see cref="CanExecute"/> is expected to return a different value.
    /// </summary>
    public class RelayCommand : ICommand
    {
        private readonly Action _execute;
        private readonly Func<bool> _canExecute;

        /// <summary>
        /// Raised when RaiseCanExecuteChanged is called.
        /// </summary>
        public event EventHandler CanExecuteChanged;

        /// <summary>
        /// Creates a new command that can always execute.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        public RelayCommand(Action execute)
            : this(execute, null)
        {
        }

        /// <summary>
        /// Creates a new command.
        /// </summary>
        /// <param name="execute">The execution logic.</param>
        /// <param name="canExecute">The execution status logic.</param>
        public RelayCommand(Action execute, Func<bool> canExecute)
        {
            if (execute == null)
                throw new ArgumentNullException("execute");
            _execute = execute;
            _canExecute = canExecute;
        }

        /// <summary>
        /// Determines whether this <see cref="RelayCommand"/> can execute in its current state.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require 
        /// data to be passed, this object can be set to null.
        /// </param>
        /// <returns>true if this command can be executed; otherwise, false.</returns>
        public bool CanExecute(object parameter)
        {
            return _canExecute == null ? true : _canExecute();
        }

        /// <summary>
        /// Executes the <see cref="RelayCommand"/> on the current command target.
        /// </summary>
        /// <param name="parameter">
        /// Data used by the command. If the command does not require 
        /// data to be passed, this object can be set to null.
        /// </param>
        public void Execute(object parameter)
        {
            _execute();
        }

        /// <summary>
        /// Method used to raise the <see cref="CanExecuteChanged"/> event
        /// to indicate that the return value of the <see cref="CanExecute"/>
        /// method has changed.
        /// </summary>
        public void RaiseCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
    }
}

Sammanfattning

Universal Windows Platform har ett robust och flexibelt kommandosystem som gör att du kan skapa appar som delar och hanterar kommandon mellan kontrolltyper, enheter och indatatyper.

Använd följande metoder när du skapar kommandon för dina Windows-appar:

  • Lyssna och hantera händelser i XAML/Code-behind.
  • Binda till en händelsehanteringsmetod, till exempel Klicka
  • Definiera din egen ICommand-implementering
  • Skapa XamlUICommand-objekt med egna värden för en fördefinierad uppsättning egenskaper
  • Skapa StandardUICommand-objekt med en uppsättning fördefinierade plattformsegenskaper och -värden

Nästa steg

Ett fullständigt exempel som visar en XamlUICommand - och StandardUICommand-implementering finns i WinUI 2-galleriets exempel.

Se även

Kontroller och mönster för Windows-appar

Samples

Ämnesexempel

Andra exempel