[英]Proper way of displaying an ObservableGroupedCollection<string, TElement> using Wpf .NET 6 and the CommunityToolkit.Mvvm Package
This question is based on:这个问题是基于:
ObservableGroupedCollection<TKey, TElement>
class from the NuGet package "CommunityToolkit.Mvvm" by Microsoft ObservableGroupedCollection<TKey, TElement>
类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 示例应用程序的摘录,右侧显示了所需的结果。
A![]() |
B![]() |
E![]() |
F ![]() |
W ![]() |
---|---|---|---|---|
a_2 ![]() |
b_0 ![]() |
e_0 ![]() |
f_0 ![]() |
w_1 ![]() |
a_1 ![]() |
f_1 ![]() |
w_0 ![]() |
||
a_0 ![]() |
f_2 ![]() |
A![]() |
B![]() |
E![]() |
F ![]() |
W ![]() |
---|---|---|---|---|
a_2 ![]() |
b_0 ![]() |
e_0 ![]() |
f_0 ![]() |
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
。
The following code is a quick and dirty example application to illustrate my problem and to provide a foundation for possible participants.以下代码是一个快速而粗略的示例应用程序,用于说明我的问题并为可能的参与者提供基础。
Requirements:要求:
namespace "yourRootNamespace".Models;
public class SomeModel
{
public string SomeString { get; set; }
public SomeModel(string _s)
{
SomeString = _s;
}
}
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));
}
}
using "yourRootNamespace".ViewModels;
using System.Windows;
namespace "yourRootNamespace";
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
<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.