Bagikan melalui


Perintah di aplikasi Windows menggunakan StandardUICommand, XamlUICommand, dan ICommand

Dalam topik ini, kami menjelaskan perintah di aplikasi Windows. Secara khusus, kami membahas bagaimana Anda dapat menggunakan kelas XamlUICommand dan StandardUICommand (bersama dengan antarmuka ICommand) untuk berbagi dan mengelola perintah di berbagai jenis kontrol, terlepas dari perangkat dan jenis input yang digunakan.

Diagram yang mewakili penggunaan umum untuk perintah bersama: beberapa permukaan UI dengan perintah 'favorit'

Bagikan perintah di berbagai kontrol, terlepas dari perangkat dan jenis input

API penting

Gambaran Umum

Perintah dapat dipanggil langsung melalui interaksi UI seperti mengklik tombol atau memilih item dari menu konteks. Mereka juga dapat dipanggil secara tidak langsung melalui perangkat input seperti akselerator keyboard, gerakan, pengenalan ucapan, atau alat otomatisasi/aksesibilitas. Setelah dipanggil, perintah kemudian dapat ditangani oleh kontrol (navigasi teks dalam kontrol edit), jendela (navigasi belakang), atau aplikasi (keluar).

Perintah dapat beroperasi pada konteks tertentu dalam aplikasi Anda, seperti menghapus teks atau membatalkan tindakan, atau dapat bebas konteks, seperti mematikan audio atau menyesuaikan kecerahan.

Gambar berikut menunjukkan dua antarmuka perintah (CommandBar dan CommandBarFlyout kontekstual mengambang) yang berbagi beberapa perintah yang sama.

Bilah Perintah diperluas
Bilah perintah

Menu konteks di galeri Foto Microsoft
Menu konteks di galeri Foto Microsoft

Interaksi perintah

Karena berbagai perangkat, jenis input, dan permukaan UI yang dapat memengaruhi bagaimana perintah dipanggil, kami sarankan untuk mengekspos perintah Anda melalui permukaan perintah sebanyak mungkin. Ini dapat mencakup kombinasi Swipe, MenuBar, CommandBar, CommandBarFlyout, dan menu konteks tradisional.

Untuk perintah penting, gunakan akselerator khusus input. Akselerator input memungkinkan pengguna melakukan tindakan dengan lebih cepat tergantung pada perangkat input yang mereka gunakan.

Berikut adalah beberapa akselerator input umum untuk berbagai jenis input:

  • Penunjuk - Tombol mouse & Pena melayang
  • Keyboard - Pintasan (tombol akses dan tombol akselerator)
  • Sentuhan - Gesek
  • Sentuh - Tarik untuk menyegarkan data

Anda harus mempertimbangkan jenis input dan pengalaman pengguna untuk membuat fungsionalitas aplikasi Anda dapat diakses secara universal. Misalnya, koleksi (terutama yang dapat diedit pengguna) biasanya menyertakan berbagai perintah tertentu yang dilakukan dengan sangat berbeda tergantung pada perangkat input.

Tabel berikut ini memperlihatkan beberapa perintah koleksi umum dan cara untuk mengekspos perintah tersebut.

Perintah Input-agnostik Akselerator mouse Akselerator keyboard Akselerator sentuh
Hapus item Menu Konteks Tombol Arahkan mouse ke arah yang lebih tinggi Kunci DEL Gesek untuk menghapus
Bendera item Menu Konteks Tombol Arahkan mouse ke arah yang lebih tinggi Ctrl+Shift+G Gesek ke bendera
Memuat ulang data Menu Konteks T/A Kunci F5 Tarik untuk menyegarkan
Favoritkan item Menu Konteks Tombol Arahkan mouse ke arah yang lebih tinggi F, Ctrl+S Geser ke favorit

Selalu sediakan menu konteks Sebaiknya sertakan semua perintah kontekstual yang relevan di menu konteks tradisional atau CommandBarFlyout, karena keduanya didukung untuk semua jenis input. Misalnya, jika perintah hanya diekspos selama peristiwa hover pointer, perintah tersebut tidak dapat digunakan pada perangkat khusus sentuh.

Perintah di aplikasi Windows

Ada beberapa cara untuk berbagi dan mengelola pengalaman perintah dalam aplikasi Windows. Anda dapat menentukan penanganan aktivitas untuk interaksi standar, seperti Klik, di code-behind (ini bisa sangat tidak efisien, tergantung pada kompleksitas UI Anda), Anda dapat mengikat pendengar peristiwa untuk interaksi standar ke handler bersama, atau Anda dapat mengikat properti Perintah kontrol ke implementasi ICommand yang menjelaskan logika perintah.

Untuk memberikan pengalaman pengguna yang kaya dan komprehensif di seluruh permukaan perintah secara efisien dan dengan duplikasi kode minimal, sebaiknya gunakan fitur pengikatan perintah yang dijelaskan dalam topik ini(untuk penanganan peristiwa standar, lihat topik peristiwa individual).

Untuk mengikat kontrol ke sumber daya perintah bersama, Anda dapat mengimplementasikan antarmuka ICommand sendiri, atau Anda dapat membangun perintah Anda dari kelas dasar XamlUICommand atau salah satu perintah platform yang ditentukan oleh kelas turunan StandardUICommand .

  • Antarmuka ICommand (Windows.UI.Xaml.Input.ICommand atau System.Windows.Input.ICommand) memungkinkan Anda membuat perintah yang sepenuhnya disesuaikan dan dapat digunakan kembali di seluruh aplikasi Anda.
  • XamlUICommand juga menyediakan kemampuan ini tetapi menyederhanakan pengembangan dengan mengekspos sekumpulan properti perintah bawaan seperti perilaku perintah, pintasan keyboard (kunci akses dan tombol akselerator), ikon, label, dan deskripsi.
  • StandardUICommand menyederhanakan hal-hal lebih lanjut dengan memungkinkan Anda memilih dari serangkaian perintah platform standar dengan properti yang telah ditentukan sebelumnya.

Penting

Dalam aplikasi UWP, perintah adalah implementasi dari antarmuka Windows.UI.Xaml.Input.ICommand (C++) atau antarmuka System.Windows.Input.ICommand (C#), tergantung pada kerangka kerja bahasa yang Anda pilih.

Pengalaman perintah menggunakan kelas StandardUICommand

Berasal dari XamlUICommand (berasal dari Windows.UI.Xaml.Input.ICommand untuk C++ atau System.Windows.Input.ICommand untuk C#), kelas StandardUICommand mengekspos serangkaian perintah platform standar dengan properti yang telah ditentukan sebelumnya seperti ikon, akselerator keyboard, dan deskripsi.

StandardUICommand menyediakan cara cepat dan konsisten untuk menentukan perintah umum seperti Save atau Delete. Yang harus Anda lakukan adalah menyediakan fungsi execute dan canExecute.

Contoh

Sampel StandardUICommand

StandardUICommandSample

Unduh kode untuk contoh ini
Sampel perintah UWP (StandardUICommand)

Dalam contoh ini, kami menunjukkan cara meningkatkan ListView dasar dengan perintah Hapus item yang diimplementasikan melalui kelas StandardUICommand, sambil mengoptimalkan pengalaman pengguna untuk berbagai jenis input menggunakan MenuBar, Kontrol geser, tombol hover, dan menu konteks.

Catatan

Sampel ini memerlukan paket NuGet Microsoft.UI.Xaml.Controls, bagian dari WinUI 2.

Xaml:

UI sampel menyertakan ListView lima item. Menu Hapus StandardUICommand terikat ke MenuBarItem, SwipeItem, AppBarButton, dan ContextFlyout.

<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>

Kode di belakang

  1. Pertama, kami menentukan ListItemData kelas yang berisi string teks dan ICommand untuk setiap ListViewItem di ListView kami.
public class ListItemData
{
    public String Text { get; set; }
    public ICommand Command { get; set; }
}
  1. Di kelas MainPage, kami menentukan kumpulan objek untuk DataTemplate dari ListViewItemTemplate.ListItemData Kami kemudian mengisinya dengan koleksi awal lima item (dengan teks dan StandardUICommand Delete terkait).
/// <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. Selanjutnya, kita menentukan handler ICommand ExecuteRequested tempat kita mengimplementasikan perintah penghapusan item.
/// <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. Terakhir, kami menentukan handler untuk berbagai peristiwa ListView, termasuk peristiwa PointerEntered, PointerExited, dan SelectionChanged . Penanganan aktivitas penunjuk digunakan untuk menampilkan atau menyembunyikan tombol Hapus untuk setiap item.
/// <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);
}

Pengalaman perintah menggunakan kelas XamlUICommand

Jika Anda perlu membuat perintah yang tidak ditentukan oleh kelas StandardUICommand , atau Anda ingin lebih mengontrol tampilan perintah, kelas XamlUICommand berasal dari antarmuka ICommand , menambahkan berbagai properti UI (seperti ikon, label, deskripsi, dan pintasan keyboard), metode, dan peristiwa untuk menentukan UI dan perilaku perintah kustom dengan cepat.

XamlUICommand memungkinkan Anda menentukan UI melalui pengikatan kontrol, seperti ikon, label, deskripsi, dan pintasan keyboard (baik tombol akses maupun akselerator keyboard), tanpa mengatur properti individual.

Contoh

Sampel XamlUICommand

XamlUICommandSample

Unduh kode untuk contoh ini
Sampel perintah UWP (XamlUICommand)

Contoh ini berbagi fungsionalitas Hapus dari contoh StandardUICommand sebelumnya, tetapi menunjukkan bagaimana kelas XamlUICommand memungkinkan Anda menentukan perintah hapus kustom dengan ikon font, label, akselerator keyboard, dan deskripsi Anda sendiri. Seperti contoh StandardUICommand, kami meningkatkan ListView dasar dengan perintah Hapus item yang diimplementasikan melalui kelas XamlUICommand, sambil mengoptimalkan pengalaman pengguna untuk berbagai jenis input menggunakan MenuBar, Kontrol geser, tombol hover, dan menu konteks.

Banyak kontrol platform menggunakan properti XamlUICommand di bawah sampul, seperti contoh StandardUICommand kami di bagian sebelumnya.

Catatan

Sampel ini memerlukan paket NuGet Microsoft.UI.Xaml.Controls, bagian dari WinUI 2.

Xaml:

UI sampel menyertakan ListView lima item. Kustom XamlUICommand CustomXamlUICommand terikat ke MenuBarItem, SwipeItem, AppBarButton, dan menu ContextFlyout.

<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>

Kode di belakang

  1. Pertama, kami menentukan ListItemData kelas yang berisi string teks dan ICommand untuk setiap ListViewItem di ListView kami.
public class ListItemData
{
    public String Text { get; set; }
    public ICommand Command { get; set; }
}
  1. Di kelas MainPage, kami menentukan kumpulan objek untuk DataTemplate dari ListViewItemTemplate.ListItemData Kami kemudian mengisinya dengan koleksi awal lima item (dengan teks dan XamlUICommand terkait).
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. Selanjutnya, kita menentukan handler ICommand ExecuteRequested tempat kita mengimplementasikan perintah penghapusan item.
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. Terakhir, kami menentukan handler untuk berbagai peristiwa ListView, termasuk peristiwa PointerEntered, PointerExited, dan SelectionChanged . Penanganan aktivitas penunjuk digunakan untuk menampilkan atau menyembunyikan tombol Hapus untuk setiap item.
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);
}

Pengalaman perintah menggunakan antarmuka ICommand

Kontrol UWP standar (tombol, daftar, pilihan, kalender, teks prediktif) memberikan dasar untuk banyak pengalaman perintah umum. Untuk daftar lengkap jenis kontrol, lihat Kontrol dan pola untuk aplikasi Windows.

Cara paling mendasar untuk mendukung pengalaman perintah terstruktur adalah dengan menentukan implementasi antarmuka ICommand (Windows.UI.Xaml.Input.ICommand untuk C++ atau System.Windows.Input.ICommand untuk C#). Instans ICommand ini kemudian dapat terikat ke kontrol seperti tombol.

Catatan

Dalam beberapa kasus, mungkin sama efisiennya untuk mengikat metode ke peristiwa Klik dan properti ke properti IsEnabled.

Contoh

Contoh antarmuka perintah

Contoh ICommand

Unduh kode untuk contoh ini
Sampel perintah UWP (ICommand)

Dalam contoh dasar ini, kami menunjukkan bagaimana satu perintah dapat dipanggil dengan klik tombol, akselerator keyboard, dan memutar roda mouse.

Kami menggunakan dua ListViews, satu diisi dengan lima item dan yang lain kosong, dan dua tombol, satu untuk memindahkan item dari ListView di sebelah kiri ke ListView di sebelah kanan, dan yang lainnya untuk memindahkan item dari kanan ke kiri. Setiap tombol terikat ke perintah yang sesuai (ViewModel.MoveRightCommand dan ViewModel.MoveLeftCommand, masing-masing), dan diaktifkan dan dinonaktifkan secara otomatis berdasarkan jumlah item dalam ListView terkait.

Kode XAML berikut mendefinisikan UI untuk contoh kami.

<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>

Berikut adalah kode di belakang untuk UI sebelumnya.

Di code-behind, kami terhubung ke model tampilan kami yang berisi kode perintah kami. Selain itu, kami mendefinisikan handler untuk input dari roda mouse, yang juga menghubungkan kode perintah kami.

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;
                }
            }
        }
    }
}

Berikut adalah kode dari model tampilan kami

Model tampilan kami adalah tempat kami menentukan detail eksekusi untuk dua perintah di aplikasi kami, mengisi satu ListView, dan menyediakan pengonversi nilai opasitas untuk menyembunyikan atau menampilkan beberapa UI tambahan berdasarkan jumlah item setiap 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();
        }
    }
}

Terakhir, inilah implementasi kami dari antarmuka ICommand

Di sini, kami mendefinisikan perintah yang mengimplementasikan antarmuka ICommand dan hanya menyampaikan fungsionalitasnya ke objek lain.

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);
            }
        }
    }
}

Ringkasan

Platform Windows Universal menyediakan sistem perintah yang kuat dan fleksibel yang memungkinkan Anda membangun aplikasi yang berbagi dan mengelola perintah di seluruh jenis kontrol, perangkat, dan jenis input.

Gunakan pendekatan berikut saat membuat perintah untuk aplikasi Windows Anda:

  • Mendengarkan dan menangani peristiwa di XAML/code-behind
  • Ikat ke metode penanganan peristiwa seperti Klik
  • Tentukan implementasi ICommand Anda sendiri
  • Membuat objek XamlUICommand dengan nilai Anda sendiri untuk sekumpulan properti yang telah ditentukan sebelumnya
  • Membuat objek StandardUICommand dengan sekumpulan properti dan nilai platform yang telah ditentukan sebelumnya

Langkah berikutnya

Untuk contoh lengkap yang menunjukkan implementasi XamlUICommand dan StandardUICommand , lihat sampel Galeri WinUI 2.

Lihat juga

Kontrol dan pola untuk aplikasi Windows

Sampel

Sampel topik

Sampel lainnya