次の方法で共有


監視可能なグループ化されたコレクション API

MVVM Toolkit には、UI にバインドできる項目のグループ化されたコレクションの操作を容易にするため、監視可能なグループ化されたコレクションとヘルパーのグループの API がいくつか用意されています。 これは、連絡先のグループ化されたリストや、ユーザーが操作できる項目の任意の種類のグループ化されたコレクションなどの UI を構築する場合に便利です。

プラットフォーム API: ObservableGroup<TKey, TElement>ReadOnlyObservableGroup<TKey, TElement>IReadOnlyObservableGroupIReadOnlyObservableGroupObservableGroupedCollection<TKey, TElement>ReadOnlyObservableGroupedCollection<TKey, TElement>ObservableGroupedCollectionExtensions

ObservableGroup<TKey, TElement> と ReadOnlyObservableGroup<TKey, TElement>

ObservableGroup<TKey, TElement>ReadOnlyObservableGroup<TKey, TElement> 型は、グループ化のサポートも提供する、ObservableCollection<T>ReadOnlyObservableCollection<T> を継承するカスタムの監視可能なコレクション型です。 これは、連絡先の一覧の表示など、項目のグループ化されたコレクションを UI にバインドする場合に特に便利です。

ObservableGroup<TKey, TElement> の機能

ObservableGroup<TKey, TElement>ReadOnlyObservableGroup<TKey, TElement> の主な機能は次のとおりです。

  • これらは ObservableCollection<T>ReadOnlyObservableCollection<T> を継承しているため、コレクション内の項目の追加、削除、変更に関して同じ通知のサポートが提供されます。
  • また、IGrouping<TKey, TElement> インターフェイスを実装しているので、このインターフェイスのインスタンスで動作するすべての既存の LINQ 拡張機能に対する引数として、インスタンスを使用できます。
  • これら 2 つのコレクション型のインスタンスを異なるレベルで抽象化できる複数のインターフェイスを、MVVM Toolkit から実装しています (IReadOnlyObservableGroupIReadOnlyObservableGroup<TKey>IReadOnlyObservableGroup<TKey, TElement>)。 これは、部分的な型情報のみがわかる、または使用できるデータ テンプレートで特に役立ちます。

ObservableGroupedCollection<TKey, TElement> と ReadOnlyObservableGroupedCollection<TKey, TElement>

ObservableGroupedCollection<TKey, TElement>ReadOnlyObservableGroupedCollection<TKey, TElement> は、各項目がグループ化されたコレクション型 (ObservableGroup<TKey, TElement> または ReadOnlyObservableGroup<TKey, TElement>) である監視可能なコレクション型であり、ILookup<TKey, TElement> も実装しています。

ObservableGroupedCollection<TKey, TElement> の機能

ObservableGroupedCollection<TKey, TElement>ReadOnlyObservableGroupedCollection<TKey, TElement> の主な機能は次のとおりです。

  • ObservableCollection<T>ReadOnlyObservableCollection<T> を継承しているので、ObservableGroup<TKey, TElement>ReadOnlyObservableGroup<TKey, TElement> と同様に、これらも項目 (この場合はグループ) が追加、削除、または変更されたときに通知を提供します。
  • LINQ の相互運用性を向上させるため、ILookup<TKey, TElement> インターフェイスを実装しています。
  • ObservableGroupedCollectionExtensions 型のすべての API を通じて、これらのコレクション内のグループや項目を簡単に操作するため、追加のヘルパー メソッドが用意されています。

ObservableGroupedCollection<TKey, TElement> の操作

次のような Contact モデルの簡単な定義があるとします。

public sealed record Contact(Name Name, string Email, Picture Picture);

public sealed record Name(string First, string Last)
{
    public override string ToString() => $"{First} {Last}";
}

public sealed record Picture(string Url);

次は、ObservableGroupedCollection<TKey, TElement> を使って連絡先のリストを設定する ViewModel です。

public class ContactsViewModel : ObservableObject
{
    /// <summary>
    /// The <see cref="IContactsService"/> instance currently in use.
    /// </summary>
    private readonly IContactsService contactsService;

    public ContactsViewModel(IContactsService contactsService) 
    {
        this.contactsService = contactsService;

        LoadContactsCommand = new AsyncRelayCommand(LoadContactsAsync);
    }

    public IAsyncRelayCommand LoadContactsCommand { get; }

    /// <summary>
    /// Gets the current collection of contacts
    /// </summary>
    public ObservableGroupedCollection<string, Contact> Contacts { get; private set; } = new();

    /// <summary>
    /// Loads the contacts to display.
    /// </summary>
    private async Task LoadContactsAsync()
    {
        var contacts = await contactsService.GetContactsAsync();

        Contacts = new ObservableGroupedCollection<string, Contact>(
            contacts.Contacts
            .GroupBy(static c => char.ToUpperInvariant(c.Name.First[0]).ToString())
            .OrderBy(static g => g.Key));

        OnPropertyChanged(nameof(Contacts));
    }
}

この場合、相対 UI は次のようになります (WinUI XAML を使用)。

<UserControl
    x:Class="MvvmSampleUwp.Views.ContactsView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:collections="using:CommunityToolkit.Mvvm.Collections"
    xmlns:contacts="using:MvvmSample.Core.Models"
    xmlns:core="using:Microsoft.Xaml.Interactions.Core"
    xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
    xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
    xmlns:system="using:System"
    NavigationCacheMode="Enabled">
    <Page.Resources>

        <!--  SemanticZoom grouped source (this connects the grouped collection to the UI)  -->
        <CollectionViewSource
            x:Name="PeopleViewSource"
            IsSourceGrouped="True"
            Source="{x:Bind ViewModel.Contacts, Mode=OneWay}" />

        <!--  Contact template  -->
        <DataTemplate x:Key="PersonListViewTemplate" x:DataType="contacts:Contact">

            <!-- Some custom template here -->
        </DataTemplate>
    </Page.Resources>

    <!--  A command to load contacts when the control is loaded in the visual tree  -->
    <interactivity:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Loaded">
            <core:InvokeCommandAction Command="{x:Bind ViewModel.LoadContactsCommand}" />
        </core:EventTriggerBehavior>
    </interactivity:Interaction.Behaviors>

    <Grid>

        <!--  Loading bar (is displayed when the contacts are loading)  -->
        <muxc:ProgressBar
            HorizontalAlignment="Stretch"
            VerticalAlignment="Top"
            Background="Transparent"
            IsIndeterminate="{x:Bind ViewModel.LoadContactsCommand.IsRunning, Mode=OneWay}" />

        <!--  Contacts view  -->
        <SemanticZoom>
            <SemanticZoom.ZoomedInView>
                <ListView
                    ItemTemplate="{StaticResource PersonListViewTemplate}"
                    ItemsSource="{x:Bind PeopleViewSource.View, Mode=OneWay}"
                    SelectionMode="Single">
                    <ListView.GroupStyle>
                        <GroupStyle HidesIfEmpty="True">

                            <!--  Header template (you can see IReadOnlyObservableGroup being used here)  -->
                            <GroupStyle.HeaderTemplate>
                                <DataTemplate x:DataType="collections:IReadOnlyObservableGroup">
                                    <TextBlock
                                        FontSize="24"
                                        Foreground="{ThemeResource SystemControlHighlightAccentBrush}"
                                        Text="{x:Bind Key}" />
                                </DataTemplate>
                            </GroupStyle.HeaderTemplate>
                        </GroupStyle>
                    </ListView.GroupStyle>
                </ListView>
            </SemanticZoom.ZoomedInView>
            <SemanticZoom.ZoomedOutView>
                <GridView
                    HorizontalAlignment="Stretch"
                    ItemsSource="{x:Bind PeopleViewSource.View.CollectionGroups, Mode=OneWay}"
                    SelectionMode="Single">
                    <GridView.ItemTemplate>

                        <!--  Zoomed out header template (IReadOnlyObservableGroup is used again)  -->
                        <DataTemplate x:DataType="ICollectionViewGroup">
                            <Border Width="80" Height="80">
                                <TextBlock
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    FontSize="32"
                                    Foreground="{ThemeResource SystemControlHighlightAccentBrush}"
                                    Text="{x:Bind Group.(collections:IReadOnlyObservableGroup.Key)}" />
                            </Border>
                        </DataTemplate>
                    </GridView.ItemTemplate>
                </GridView>
            </SemanticZoom.ZoomedOutView>
        </SemanticZoom>
    </Grid>
</UserControl>

これにより、連絡先のグループ化された一覧が表示され、各グループでは含まれる連絡先のイニシャルがタイトルとして使われます。

MVVM Toolkit サンプル アプリで上のスニペットを実行すると、次の UI が生成されます。

連絡先ビューのサンプル

  • サンプル アプリ (複数の UI フレームワーク向け) を確認して、MVVM Toolkit の実際の動作を確認してください。
  • 単体テストで、その他の例を確認することもできます。