简体   繁体   English

显示 ObservableGroupedCollection 的正确方法<string, telement>使用 Wpf .NET 6 和 CommunityToolkit.Mvvm 包</string,>

[英]Proper way of displaying an ObservableGroupedCollection<string, TElement> using Wpf .NET 6 and the CommunityToolkit.Mvvm Package

ObservableGroupedCollection in Wpf .NET 6 Wpf .NET 6 中的 ObservableGroupedCollection

This question is based on:这个问题是基于:

  • A Wpf project using .NET 6使用.NET 6Wpf项目
  • The ObservableGroupedCollection<TKey, TElement> class from the NuGet package "CommunityToolkit.Mvvm" by Microsoft Microsoft 的 NuGet 包“CommunityToolkit.Mvvm”中的ObservableGroupedCollection<TKey, TElement>
  • striktly obeying the MVVM pattern严格遵守MVVM 模式

While tinkering with the relatively new CommunityToolkit.Mvvm I came across the ObservableGroupedCollection<TKey, TElement> class which in Wpf is rather undocumented.在修补相对较新的CommunityToolkit.Mvvm时,我遇到了ObservableGroupedCollection<TKey, TElement>类,该类在 Wpf 中没有记录。 My Wpf knowledge is poor at best - I intended to use this as a learning project - and I was unable to transfer the existing UWP xaml code into a working Wpf sample application.我的 Wpf 知识充其量是贫乏的 - 我打算将其用作学习项目 - 我无法将现有的 UWP xaml 代码转移到工作的 Wpf 示例应用程序中。
The sample app referenced in the blog post above uses a CollectionViewSource bound to an ObservableGroupedCollection<TKey, TElement> to display a grouped list of contacts within a scrollable control.上面博客文章中引用的示例应用程序使用绑定到ObservableGroupedCollection<TKey, TElement>CollectionViewSource在可滚动控件中显示分组的联系人列表。 My attempts to replicate this behavior within a Wpf .NET 6 app resulted in only the first values of each collection being displayed, rather than the entire range.我尝试在 Wpf .NET 6 应用程序中复制此行为导致仅显示每个集合的第一个值,而不是整个范围。

What is the proper way to display all entries in a grouped fashion, while obeying to the MVVM pattern?!在遵守 MVVM 模式的同时,以分组方式显示所有条目的正确方法是什么?!

The following image shows an excerpt from the Microsoft Store sample application on the left and the desired result on the right.下图左侧显示了 Microsoft Store 示例应用程序的摘录,右侧显示了所需的结果。

SampleApp 与 Desired

Results from the sample code below以下示例代码的结果

Results when iterating manually through the groups and their collections:手动遍历组及其集合时的结果:

A一种 B E F F W W
a_2 a2 b_0 b_0 e_0 e_0 f_0 f_0 w_1 w_1
a_1 a_1 f_1 f_1 w_0 w_0
a_0 a_0 f_2 f_2

Values displayed in the actual ListView:实际 ListView 中显示的值:

A一种 B E F F W W
a_2 a2 b_0 b_0 e_0 e_0 f_0 f_0 w_1 w_1

显示值 These are obviously values that got scraped off the "top" of the collections.这些显然是从集合的“顶部”刮掉的值。

What puzzles me is the fact that the SemanticZoom used in the original Sample App (.xaml - UWP) and the corresponding ViewModel.cs is somehow able to display ALL entries instead of scraping off the first element of the collection.我困惑的是,原始示例应用程序 (.xaml - UWP)中使用的SemanticZoom和相应的ViewModel.cs以某种方式能够显示所有条目,而不是刮掉集合的第一个元素。 While still using a model based DataTemplate .同时仍在使用基于模型的DataTemplate

Sample code示例代码

The following code is a quick and dirty example application to illustrate my problem and to provide a foundation for possible participants.以下代码是一个快速而粗略的示例应用程序,用于说明我的问题并为可能的参与者提供基础。

Requirements:要求:

  • Wpf Project -> .NET 6 Wpf 项目 -> .NET 6
  • NuGet package: CommunityToolkit.Mvvm by Microsoft NuGet 包:Microsoft 的CommunityToolkit.Mvvm
  • 2 new folders: Models and ViewModels 2 个新文件夹:Models 和 ViewModels
  • Replace all instances of "yourRootNamespace" with your actual root namespace“yourRootNamespace”的所有实例替换为您的实际根命名空间

SomeModel.cs SomeModel.cs

namespace "yourRootNamespace".Models;

public class SomeModel
{

    public string SomeString { get; set; }

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

MainWindowViewModel.cs 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主窗口.xaml.cs

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

namespace "yourRootNamespace";

public partial class MainWindow : Window
{

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

MainWindow.xaml主窗口.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>

I am not sure about CommunityToolkit developer's intension, it works by specifying CollectionViewSource.GroupDescriptions to CollectionViewSource .我不确定 CommunityToolkit 开发人员的意图,它通过将CollectionViewSource指定为CollectionViewSource.GroupDescriptions来工作。

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

I think there is difference between WPF and UWP, or i don't know where, my UI platform knowledge is poor: CollectionViewSource in dotnet 6 WPF does not have IsSourceGrouped property.我认为 WPF 和 UWP 之间存在差异,或者我不知道在哪里,我的 UI 平台知识很差:Dotnet 6 WPF 中的CollectionViewSource没有IsSourceGrouped属性。 Code in Microsoft samples sets this property to true , but your code does not - because WPF doesn't provide it. Microsoft 示例中的代码将此属性设置为true ,但您的代码没有 - 因为 WPF 不提供它。

I was getting same behavior and went to compare what's different, line by line and found the absense of IsSourceGrouped .我得到了相同的行为,然后逐行比较不同之处,发现IsSourceGrouped The only explanation i can think of, is that grouped collection is a collection of nested collections, so internals of ListView/CollectionViewSource have to properly enumerate it in-depth, and that's what is not happening in your case.我能想到的唯一解释是,分组集合是嵌套集合的集合,因此 ListView/CollectionViewSource 的内部必须正确地深入枚举它,而这在您的案例中不会发生。

So the answer would be: this feature is missing in a framework version you are using, so you have to resort to workarounds, and not strictly clean MVVM.所以答案是:您正在使用的框架版本中缺少此功能,因此您必须求助于变通方法,而不是严格清洁 MVVM。

UPD: I managed to do clean xaml-only grouping with only an ObservableCollection<T> in ViewModel. UPD:我设法在 ViewModel 中仅使用ObservableCollection<T>进行干净的仅 xaml 分组。 ListView gets groups and items from a ItemsSource="{Binding Source={StaticResource CustomViewSource}} which controls how to group, sort and so on: ListView 从控制如何分组、排序等的ItemsSource="{Binding Source={StaticResource CustomViewSource}}获取组和项目:

<CollectionViewSource x:Key="CustomViewSource" Source="{Binding OnlineMods}" IsLiveGroupingRequested="True">
    <CollectionViewSource.GroupDescriptions>
        <PropertyGroupDescription PropertyName="YourPropertyForGrouping" />
    </CollectionViewSource.GroupDescriptions>
    <CollectionViewSource.SortDescriptions>
        <componentModel:SortDescription PropertyName="YourPropertyForOrdering" />
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

It is also possible to add predefined empty groups there to be always displayed and do other tricks.也可以在其中添加预定义的空组以始终显示并执行其他技巧。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 CommunityToolkit.Mvvm 调用事件 - How to call events using CommunityToolkit.Mvvm 如何将 CommunityToolkit.Mvvm 中的源代码生成器用于 .NET 框架 4.7.2 WPF 应用程序 - How to use the source generators from CommunityToolkit.Mvvm for a .NET Framework 4.7.2 WPF Application 如何在 C# 中使用 Community - How do I bind an object that is periodically modified to a treeview in C# WPF using the CommunityToolkit.MVVM? 使用 CommunityToolkit.Mvvm 在 ObservableProperty 更改时调用方法 - Call method when ObservableProperty changes using CommunityToolkit.Mvvm 使用 CommunityToolkit.Mvvm 通知 CanExecuteChanged 的 RelayCommand 时出现 StackOverflow 异常 - StackOverflow Exception when notifying RelayCommand of CanExecuteChanged using CommunityToolkit.Mvvm C# CommunityToolkit.Mvvm ObservableProperty 列表 - C# CommunityToolkit.Mvvm ObservableProperty on a list 使用 CommunityToolkit.Mvvm 处理可观察对象属性 - Handling observable object properties with CommunityToolkit.Mvvm 无法在使用 CommunityToolkit.Mvvm 的视图模型中使用 ICommand 属性 - Can't use ICommand attribute in view model using CommunityToolkit.Mvvm CommunityToolkit.MVVM 命名空间“命名空间”已包含“类型”的定义 - CommunityToolkit.MVVM The namespace 'namespace' already contains a definition for 'type' 在 WPF 中实现 MVVM 模式的正确方法 - Proper way of implementing MVVM Pattern in WPF
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM