Reputation: 689
My Setup
I'm using ListCollectionView
to sort and group my data in ListBox
.
My model:
public class Game
{
public string Name
{
get; set;
}
public List<string> Categories
{
get; set;
}
}
What I want do to is to group by Categories
, where groups in view are sorted alphabetically. And also sort all items in groups by Name
.
Also I want to have group representing null or empty Categories at bottom of view.
For example:
Category1
Name2
Name3
Category2
Name2
Name3
Name4
Name5
Null/Empty
Name1
Name6
Since Categories
is List<string>
it means that any item can be in one or more groups or none.
Problem
I don't know how to properly implement sorting for categories. I know that I have to set multiple SortDescriptions
to sort by Categories
first and by Name
second, but how do I do that?
WPF doesn't known how to sort List<string>
groups by default so I tried to change it to custom List which implements IComparable
, but that can't handle null values (comparer cannot be called if one of the objects is null).
I also tried to implement CustomSort
on my view, but I thinks it's not impossible to handle my situation that way (where one item can be in multiple groups).
Update full repro code
The result is:
Null
Game1
Category2
Game2
Game3
Category1
Game3
Game4
And I want it to be:
Category1
Game3
Game4
Category2
Game2
Game3
Null
Game1
Code:
public partial class MainWindow : Window
{
public class Game
{
public string Name
{
get; set;
}
public List<string> Categories
{
get; set;
}
}
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var games = new List<Game>()
{
new Game()
{
Name = "Game1",
},
new Game()
{
Name = "Game2",
Categories = new List<string>() { "Category2" }
},
new Game()
{
Name = "Game3",
Categories = new List<string>() { "Category1", "Category2" }
},
new Game()
{
Name = "Game4",
Categories = new List<string>() { "Category1" }
}
};
var view = (ListCollectionView)CollectionViewSource.GetDefaultView(games);
view.SortDescriptions.Add(new SortDescription("Name", ListSortDirection.Ascending));
view.GroupDescriptions.Add(new PropertyGroupDescription("Categories"));
MainList.ItemsSource = view;
}
}
And XAML:
<Window x:Class="grouping.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:grouping"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<ListBox Name="MainList">
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Expander Header="{Binding Name, TargetNullValue='Null'}" IsExpanded="True">
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Upvotes: 0
Views: 1820
Reputation: 13438
Basically, your data are not in suitable format for displaying in WPF. You want the same entry to appear in multiple places (once per group), while the typical itemssource will display each item once, no matter the sort details.
So you should translate your data to a representation that is more natural to WPF. For example (with custom comparer for sorting):
public class GameListEntry : IComparable<GameListEntry>
{
public string Name { get; set; }
public string Category { get; set; }
public int CompareTo(GameListEntry other)
{
if (Category == other.Category)
{
return 0;
}
if (Category == "Null / Empty")
{
return 1;
}
if (other.Category == "Null / Empty")
{
return -1;
}
return Category.CompareTo(other.Category);
}
}
List<Game> games = ...;
var listedGameEntries = games.SelectMany(x => x.Categories.DefaultIfEmpty().Select(c => new GameListEntry { Name = x.Name, Category = string.IsNullOrEmpty(c) ? "Null / Empty" : c }));
var groupedGames = listedGameEntries.GroupBy(x => x.Category);
The CompareTo might need to be enhanced or replaced by a different mechanism in order to make the sorting and grouping work the way it should.
Edit: added DefaultIfEmpty()
in order to ensure one entry even with an empty list of categories.
Upvotes: 2