我正在嘗試顯示一個 ListBox,其中包含按名為的屬性分組的專案Status。這很容易使用CollectionViewSource.GroupDescriptions,但是當我想顯示這樣的空組時遇到問題:

我發現了如何將組名稱添加到組描述中,但它們不是唯一的。運行此代碼會顯示所有狀態:
var collectionViewSource = (CollectionViewSource)FindResource("Source");
var groupDescription = new PropertyGroupDescription("Status");
foreach (var name in Enum.GetNames(typeof(Status)))
{
groupDescription.GroupNames.Add(name);
}
collectionViewSource.GroupDescriptions.Add(groupDescription);
但是所有包含專案的組都被重復。此外,這些手動創建的組內部沒有專案,并且與 WPF 創建的組完全不同:

我想我可以收聽CollectionChanged并GroupNames ObservableCollection手動添加或洗掉這些自定義組名,但GroupNames總是空的(除非組名是像上面的代碼片段那樣手動添加的),所以CollectionChanged永遠不會被解雇。
這是沒有自定義 GroupName 邏輯的代碼:
C#:
public enum Status
{
Present,
Sick,
Vacation,
BusinessTrip
}
public class ViewModel
{
public string Id { get; }
public Status Status { get; }
public ViewModel(string id, Status status)
{
Id = id;
Status = status;
}
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new List<ViewModel> {
new ("Zack", Status.Present),
new ("Adam", Status.Sick),
new ("Matt", Status.Present),
new ("Nick", Status.Present),
new ("Boss", Status.Vacation),
new ("Phil", Status.Vacation)
};
var collectionViewSource = (CollectionViewSource)FindResource("Source");
var groupDescription = new PropertyGroupDescription("Status");
collectionViewSource.GroupDescriptions.Add(groupDescription);
}
}
XAML:
<Window x:Class="EmptyGroups.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<CollectionViewSource x:Key="Source" Source="{Binding}"/>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource Source}}">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
<TextBlock Margin="10" Background="Aqua" Text="{Binding ItemCount}"/>
</StackPanel>
</Expander.Header>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="50 10" Text="{Binding Id}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
</ListBox>
</Grid>
</Window>
有任何想法嗎?我覺得有一種不同的更好的方式來獲得這個Empty Group功能。
謝謝!
uj5u.com熱心網友回復:
我找到了一個解決方案,但它仍然感覺很糟糕......
創建一個繼承自 CollectionViewSource 的類:
public class CustomCollectionViewSource : CollectionViewSource
{
public static readonly DependencyProperty AllGroupNamesProperty = DependencyProperty.Register(
"AllGroupNames", typeof(List<string>), typeof(CustomCollectionViewSource),
new PropertyMetadata(new List<string>(), OnAllGroupNamesChanged));
private static void OnAllGroupNamesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
((CustomCollectionViewSource) d).UpdateGroupNames();
public List<string> AllGroupNames
{
get => (List<string>) GetValue(AllGroupNamesProperty);
set => SetValue(AllGroupNamesProperty, value);
}
public CustomCollectionViewSource()
{
DependencyPropertyDescriptor
.FromProperty(ViewProperty, typeof(CustomCollectionViewSource))
.AddValueChanged(this, OnViewChanged);
}
private void OnViewChanged(object sender, EventArgs e) => UpdateGroupNames();
private void OnViewCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => UpdateGroupNames();
private void UpdateGroupNames()
{
if (View == null) return;
if (View.GroupDescriptions.Count != 1)
throw new Exception("CustomCollectionViewSource must have exactly one GroupDescription!");
View.CollectionChanged -= OnViewCollectionChanged;
try
{
var groupDescription = View.GroupDescriptions.First();
groupDescription.GroupNames.Clear();
var names = View.Groups.Cast<CollectionViewGroup>().Select(cvg => cvg.Name.ToString());
foreach (var s in AllGroupNames.Except(names))
groupDescription.GroupNames.Add(s);
}
finally
{
View.CollectionChanged = OnViewCollectionChanged;
}
}
}
然后用它代替CollectionViewSource:
<emptyGroups:CustomCollectionViewSource x:Key="Source" Source="{Binding}">
<emptyGroups:CustomCollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Status"/>
</emptyGroups:CustomCollectionViewSource.GroupDescriptions>
</emptyGroups:CustomCollectionViewSource>
那么有兩種方法可以指定組:
在后面的代碼中手動分配它們:
var collectionViewSource = (CustomCollectionViewSource)FindResource("Source"); foreach (var status in Enum.GetNames(typeof(Status))) { collectionViewSource.AllGroupNames.Add(status); }使用 XAML:
<emptyGroups:CustomCollectionViewSource x:Key="Source" Source="{Binding}"> <emptyGroups:CustomCollectionViewSource.AllGroupNames> <system:String>Present</system:String> <system:String>Sick</system:String> <system:String>Vacation</system:String> <system:String>BusinessTrip</system:String> </emptyGroups:CustomCollectionViewSource.AllGroupNames> <emptyGroups:CustomCollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="Status"/> </emptyGroups:CustomCollectionViewSource.GroupDescriptions> </emptyGroups:CustomCollectionViewSource>
一個問題是空組總是顯示在包含專案的組的頂部。這可以通過設定groupDescription.CustomSortin CustomCollectionViewSourceinside來解決UpdateGroupNames:
private void UpdateGroupNames() {
if (View == null) return;
if (View.GroupDescriptions.Count != 1)
throw new Exception("CustomCollectionViewSource must have exactly one GroupDescription!");
View.CollectionChanged -= OnViewCollectionChanged;
try {
var groupDescription = View.GroupDescriptions.First();
groupDescription.GroupNames.Clear();
var names = View.Groups.Cast<CollectionViewGroup>().Select(cvg => cvg.Name.ToString());
foreach (var s in AllGroupNames.Except(names))
groupDescription.GroupNames.Add(s);
groupDescription.CustomSort = new SortByGroupCount(); // Add if you want to order the groups by items.
} finally {
View.CollectionChanged = OnViewCollectionChanged;
}
}
這里是SortByGroupCount:
public class SortByGroupCount : IComparer {
public int Compare(object x, object y) {
if (x is CollectionViewGroup xGroup && y is CollectionViewGroup yGroup) {
return xGroup.Items.Count > yGroup.Items.Count ? 1 : -1;
}
return 0;
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/496857.html
上一篇:如何使用依賴于框架的Windows應用程式打包專案打包.NET6WPF應用程式?
下一篇:如何創建具有圓角的框架?
