Правильный способ отображения ObservableGroupedCollection<string, TElement> с использованием Wpf .NET 6 и пакета CommunityToolkit.Mvvm

ObservableGroupedCollection в Wpf.NET 6

Этот вопрос основан на:

  • Проект Wpf с использованием .NET 6
  • Класс из пакета NuGet «CommunityToolkit.Mvvm» от Microsoft.
  • строгое соблюдение шаблона MVVM

Возясь с относительно новым CommunityToolkit.Mvvm, я наткнулся на класс, который в Wpf довольно недокументирован. Мои знания Wpf в лучшем случае скудны — я намеревался использовать это в качестве учебного проекта — и мне не удалось перенести существующий код UWP xaml в рабочий пример приложения Wpf.
Пример приложения, упомянутый в сообщении блога выше, используетCollectionViewSourceпривязан кObservableGroupedCollection<TKey, TElement>для отображения сгруппированного списка контактов в прокручиваемом элементе управления. Мои попытки воспроизвести это поведение в приложении Wpf .NET 6 привели к отображению только первых значений каждой коллекции, а не всего диапазона.

Как правильно отобразить все записи сгруппированным образом, соблюдая шаблон MVVM?!

На следующем изображении показан отрывок из примера приложения Microsoft Store слева и желаемый результат справа.

Результаты из приведенного ниже примера кода

Результаты при переборе групп и их коллекций вручную:

Это, очевидно, значения, которые были соскоблины с «верхней части» коллекций.

Меня озадачивает тот факт, чтоSemanticZoomиспользуемый в исходном примере приложения (.xaml — UWP), и соответствующий ViewModel.cs каким-то образом может отображать ВСЕ записи, а не удалять первый элемент коллекции. Все еще используя модель, основаннуюDataTemplate.

Образец кода

Следующий код представляет собой быстрый и грязный пример приложения, иллюстрирующий мою проблему и обеспечивающий основу для возможных участников.

Требования:

  • Проект Wpf -> .NET 6
  • Пакет NuGet: CommunityToolkit.Mvvm от Microsoft
  • 2 новые папки: Models и ViewModels
  • Замените все экземпляры «yourRootNamespace» вашим фактическим корневым пространством имен.

SomeModel.cs

      namespace "yourRootNamespace".Models;

public class SomeModel
{

    public string SomeString { get; set; }

    public SomeModel(string _s)
    {
        SomeString = _s;
    }
}

MainWindowViewModel.cs

      using CommunityToolkit.Mvvm.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
using "yourRootNamespace".Models;
using System.Collections.Generic;
using System.Linq;

namespace "yourRootNamespace".ViewModels;

public partial class MainWindowViewModel : ObservableObject
{

    [ObservableProperty]
    private ObservableGroupedCollection<string, SomeModel>? m_someObservableGroupedCollection;

    public MainWindowViewModel()
    {
        List<SomeModel> tempList = new List<SomeModel>()
        {
            new SomeModel("w_1"),
            new SomeModel("b_0"),
            new SomeModel("a_2"),
            new SomeModel("e_0"),
            new SomeModel("f_0"),
            new SomeModel("f_1"),
            new SomeModel("a_1"),
            new SomeModel("a_0"),
            new SomeModel("w_0"),
            new SomeModel("f_2")
        };

        m_someObservableGroupedCollection = new ObservableGroupedCollection<string, SomeModel>(tempList
            .GroupBy(c => char.ToUpperInvariant(c.SomeString[0]).ToString())
            .OrderBy(g => g.Key));
    }
}

MainWindow.xaml.cs

      using "yourRootNamespace".ViewModels;
using System.Windows;

namespace "yourRootNamespace";

public partial class MainWindow : Window
{

    public MainWindow()
    {
        InitializeComponent();
        DataContext = new MainWindowViewModel();
    }
}

MainWindow.xaml

      <Window x:Class=""yourRootNamespace".MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:"yourRootNamespace""
        xmlns:collections="clr-namespace:CommunityToolkit.Mvvm.Collections;assembly=CommunityToolkit.Mvvm"
        xmlns:viewmodels="clr-namespace:"yourRootNamespace".ViewModels"
        xmlns:models="clr-namespace:"yourRootNamespace".Models"
        d:DataContext="{d:DesignInstance Type=viewmodels:MainWindowViewModel}"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>

        <CollectionViewSource
            x:Key="SomeListViewSource"
            Source="{Binding SomeObservableGroupedCollection}"
            IsLiveGroupingRequested="True"/>

        <DataTemplate
            x:Key="SomeTemplate"
            DataType="{x:Type models:SomeModel}">
            <TextBlock Text="{Binding SomeString}"/>
        </DataTemplate>

    </Window.Resources>

    <Grid>
        <ListView
            ItemTemplate="{StaticResource SomeTemplate}"
            ItemsSource="{Binding Source={StaticResource SomeListViewSource}, Mode=OneWay}"
            SelectionMode="Single">
            <ListView.GroupStyle>
                <GroupStyle
                    HidesIfEmpty="True">
                    <GroupStyle.HeaderTemplate>
                        <DataTemplate
                            DataType="{x:Type collections:IReadOnlyObservableGroup}">
                            <TextBlock Text="{Binding Key}"/>
                        </DataTemplate>
                    </GroupStyle.HeaderTemplate>
                </GroupStyle>
            </ListView.GroupStyle>
        </ListView>
    </Grid>

</Window>

1 ответ

Я не уверен в намерениях разработчика CommunityToolkit, он работает, указавCollectionViewSource.GroupDescriptionsкCollectionViewSource.

      <Window.Resources>
    <CollectionViewSource x:Key="SomeListViewSource"
                          Source="{Binding SomeObservableGroupedCollection}"
                          IsLiveGroupingRequested="True">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="Key"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>

    <DataTemplate x:Key="SomeTemplate">
        <TextBlock Text="{Binding SomeString}"/>
    </DataTemplate>
</Window.Resources>

<Grid>
    <ListView ItemTemplate="{StaticResource SomeTemplate}"
              ItemsSource="{Binding Source={StaticResource SomeListViewSource}, Mode=OneWay}"
              SelectionMode="Single">
        <ListView.GroupStyle>
            <GroupStyle HidesIfEmpty="True">
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Foreground="Red"
                                   Text="{Binding Name}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </ListView.GroupStyle>
    </ListView>
</Grid>
Другие вопросы по тегам