sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

C# CommunityToolkit.Mvvm の学習9 collections

ObservableGroup<TKey, TElement> and ReadOnlyObservableGroup<TKey, TElement>

ObservableGroup<TKey, TElement>, ReadOnlyObservableGroup<TKey, TElement> は、それぞれ ObservableCollection, ReadOnlyObservableCollection を継承したカスタム observable collection 型です。グループ化のサポートも提供しています。

連絡先のリストを表示するなど、グループ化されたアイテムのコレクションを UI に binding する場合に特別便利です。

Sample

public class MyViewModel : ObservableObject
{
    public MyViewModel()
    {
        IncrementCounterCommand = new RelayCommand(IncrementCounter);
    }

    private int counter;

    public int Counter
    {
        get => counter;
        private set => SetProperty(ref counter, value);
    }

    public ICommand IncrementCounterCommand { get; }

    private void IncrementCounter() => Counter++;
}
<Grid Height="480">
    <Grid.Resources>

        <!--  Shared menu flyout for all contacts  -->
        <MenuFlyout x:Key="ContactMenuFlyout">
            <MenuFlyoutItem
                Command="{x:Bind ViewModel.DeleteContactCommand}"
                CommandParameter="{Binding}"
                Text="Remove contact">
                <MenuFlyoutItem.Icon>
                    <SymbolIcon Symbol="Delete" />
                </MenuFlyoutItem.Icon>
            </MenuFlyoutItem>
        </MenuFlyout>

        <!--  SemanticZoom grouped sourc  -->
        <CollectionViewSource
            x:Name="PeopleViewSource"
            IsSourceGrouped="True"
            Source="{x:Bind ViewModel.Contacts, Mode=OneWay}" />

        <!--  Contact template  -->
        <DataTemplate x:Key="PersonListViewTemplate" x:DataType="contacts:Contact">
            <Grid ContextFlyout="{StaticResource ContactMenuFlyout}">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Ellipse
                    x:Name="Ellipse"
                    Grid.RowSpan="2"
                    Width="32"
                    Height="32"
                    Margin="6"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
                    <Ellipse.Fill>
                        <ImageBrush ImageSource="{x:Bind Picture.Url}" />
                    </Ellipse.Fill>
                </Ellipse>
                <TextBlock
                    Grid.Column="1"
                    Margin="12,6,0,0"
                    x:Phase="1"
                    Style="{ThemeResource BaseTextBlockStyle}"
                    Text="{x:Bind Name}" />
                <TextBlock
                    Grid.Row="1"
                    Grid.Column="1"
                    Margin="12,0,0,6"
                    x:Phase="2"
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="{x:Bind Email}" />
            </Grid>
        </DataTemplate>
    </Grid.Resources>

    <!--  Loading bar  -->
    <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">
                        <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>
                    <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>

ObservableGroup<TKey, TElement> features

ObservableGroup<TKey, TElement> および ReadOnlyObservableGroup<TKey, TElement> の主な特徴は次のとおりです:

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

ObservableGroupedCollection<TKey, TElement> and ReadOnlyObservableGroupedCollection<TKey, TElement>

ObservableGroupedCollection<TKey, TElement> および ReadOnlyObservableGroupedCollection<TKey, TElement> は、各項目がグループ化された observable collection の型です。また ILookup<TKey, TElement> も実装しています。

ObservableGroupedCollection<TKey, TElement> features

ObservableGroupedCollection<TKey,TElement> および ReadOnlyObservableGroupedCollection<TKey,TElement> には、主に以下の特徴があります:

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

collections の章は現在おそらく web 上では公開されていません。内容も確認程度のことなので、新しく重要な情報はそれほどないように思いました。

参考

レガシーコードとどう付き合うか

レガシーコードとどう付き合うか

Amazon