Skip to content

Commit 2b7be4c

Browse files
committed
refactor column filter logic to improve peformance
1 parent 3437fdf commit 2b7be4c

3 files changed

Lines changed: 100 additions & 38 deletions

File tree

src/ColumnFilterHandler.cs

Lines changed: 72 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
34
using System.Linq;
45
using WinUI.TableView.Extensions;
56

@@ -25,36 +26,78 @@ public virtual IList<TableViewFilterItem> GetFilterItems(TableViewColumn column,
2526
{
2627
if (column is { TableView.ItemsSource: { } })
2728
{
28-
var collectionView = new CollectionView(column.TableView.ItemsSource);
29+
var collectionView = new CollectionView(liveShapingEnabled: false);
2930
collectionView.FilterDescriptions.AddRange(
3031
column.TableView.FilterDescriptions.Where(
3132
x => x is not ColumnFilterDescription columnFilter || columnFilter.Column != column));
33+
if (searchText is { Length: > 0 })
34+
{
35+
collectionView.FilterDescriptions.Add(new FilterDescription(default, item =>
36+
{
37+
var value = column.GetCellContent(item);
38+
return string.IsNullOrEmpty(searchText) ||
39+
value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true;
40+
}));
41+
}
42+
43+
collectionView.Source = column.TableView.ItemsSource;
3244

33-
return [.. collectionView.Select(column.GetCellContent)
34-
.Select(x => IsBlank(x) ? null : x)
35-
.GroupBy(x => x)
36-
.OrderBy(x => x.Key)
37-
.Select(x =>
38-
{
39-
var value = x.Key;
40-
value ??= TableViewLocalizedStrings.BlankFilterValue;
41-
var isSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) ||
42-
(column.IsFiltered && SelectedValues[column].Contains(value));
43-
44-
return string.IsNullOrEmpty(searchText)
45-
|| value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true
46-
? new TableViewFilterItem(isSelected, value, x.Count())
47-
: null;
48-
49-
})
50-
.OfType<TableViewFilterItem>()
51-
.OrderByDescending(x => _tableView.ShowFilterItemsCount ? x.Count : 0)];
45+
var items = _tableView.ShowFilterItemsCount ?
46+
GetFilterItemsWithCount(column, searchText, collectionView) :
47+
GetFilterItems(column, searchText, collectionView);
48+
49+
return [.. items];
5250
}
5351

5452
return [];
5553
}
5654

57-
private static bool IsBlank(object? value)
55+
private IEnumerable<TableViewFilterItem> GetFilterItemsWithCount(TableViewColumn column, string? searchText, CollectionView collectionView)
56+
{
57+
var nullCount = 0;
58+
var isNullItemSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) ||
59+
(column.IsFiltered && SelectedValues[column].Contains(null));
60+
var filterValues = new SortedDictionary<object, int>();
61+
62+
foreach (var item in collectionView)
63+
{
64+
var value = column.GetCellContent(item);
65+
66+
if (IsBlank(value)) nullCount++;
67+
else if (filterValues.TryGetValue(value, out var count)) filterValues[value] = ++count;
68+
else filterValues.Add(value, 1);
69+
}
70+
71+
IEnumerable<TableViewFilterItem> nullFilterItem = nullCount > 0 ? [new TableViewFilterItem(isNullItemSelected, null, nullCount)] : [];
72+
73+
return [.. nullFilterItem,.. filterValues.Select(x =>
74+
{
75+
var isSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) ||
76+
(column.IsFiltered && SelectedValues[column].Contains(x.Key));
77+
return new TableViewFilterItem(isSelected, x.Key, x.Value);
78+
}) .OrderByDescending(x=>x.Count)];
79+
}
80+
81+
private IEnumerable<TableViewFilterItem> GetFilterItems(TableViewColumn column, string? searchText, CollectionView collectionView)
82+
{
83+
var filterValues = new SortedSet<object?>();
84+
85+
foreach (var item in collectionView)
86+
{
87+
var value = column.GetCellContent(item);
88+
value = IsBlank(value) ? null : value;
89+
filterValues.Add(value);
90+
}
91+
92+
return [.. filterValues.Select(x =>
93+
{
94+
var isSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) ||
95+
(column.IsFiltered && SelectedValues[column].Contains(x));
96+
return new TableViewFilterItem(isSelected, x, 0);
97+
})];
98+
}
99+
100+
private static bool IsBlank([NotNullWhen(false)] object? value)
58101
{
59102
return value == null ||
60103
value == DBNull.Value ||
@@ -65,38 +108,33 @@ private static bool IsBlank(object? value)
65108
/// <inheritdoc/>
66109
public virtual void ApplyFilter(TableViewColumn column)
67110
{
68-
if (column is { TableView: { } })
111+
if (column is { TableView.CollectionView: CollectionView { } collectionView })
69112
{
113+
using var defer = collectionView.DeferRefresh();
70114
column.TableView.DeselectAll();
71115

72-
if (column.IsFiltered)
73-
{
74-
column.TableView.RefreshFilter();
75-
}
76-
else
116+
if (!column.IsFiltered)
77117
{
78118
var boundColumn = column as TableViewBoundColumn;
79119

80120
column.IsFiltered = true;
81-
column.TableView.FilterDescriptions.Add(new ColumnFilterDescription(
121+
collectionView.FilterDescriptions.Add(new ColumnFilterDescription(
82122
column,
83123
boundColumn?.PropertyPath,
84124
(o) => Filter(column, o)));
85125
}
86-
column.TableView.RefreshFilter();
87-
column.TableView.EnsureAlternateRowColors();
88126
}
89127
}
90128

91129
/// <inheritdoc/>
92130
public virtual void ClearFilter(TableViewColumn? column)
93131
{
94-
if (column is { TableView: { } })
132+
if (column is { TableView.CollectionView: CollectionView { } collectionView })
95133
{
134+
using var defer = collectionView.DeferRefresh();
96135
column.IsFiltered = false;
97-
column.TableView.FilterDescriptions.RemoveWhere(x => x is ColumnFilterDescription columnFilter && columnFilter.Column == column);
136+
collectionView.FilterDescriptions.RemoveWhere(x => x is ColumnFilterDescription columnFilter && columnFilter.Column == column);
98137
SelectedValues.RemoveWhere(x => x.Key == column);
99-
column.TableView.RefreshFilter();
100138
}
101139
else
102140
{
@@ -117,7 +155,7 @@ public virtual void ClearFilter(TableViewColumn? column)
117155
public virtual bool Filter(TableViewColumn column, object? item)
118156
{
119157
var value = column.GetCellContent(item);
120-
value = IsBlank(value) ? TableViewLocalizedStrings.BlankFilterValue : value!;
158+
value = IsBlank(value) ? null : value!;
121159
return SelectedValues[column].Contains(value);
122160
}
123161

src/TableViewColumnHeader.FilterItem.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public partial class TableViewFilterItem : INotifyPropertyChanged
1818
/// <param name="isSelected">Indicates whether the filter item is selected.</param>
1919
/// <param name="value">The value of the filter item.</param>
2020
/// <param name="count">The count of occurrences for the filter item.</param>
21-
public TableViewFilterItem(bool isSelected, object value, int count = 1)
21+
public TableViewFilterItem(bool isSelected, object? value, int count = 1)
2222
{
2323
IsSelected = isSelected;
2424
Value = value;
@@ -41,10 +41,17 @@ public bool IsSelected
4141
/// <summary>
4242
/// Gets the value of the filter item.
4343
/// </summary>
44-
public object Value { get; }
44+
public object? Value { get; }
4545

4646
/// <summary>
4747
/// Gets or sets the count of occurrences for the filter item.
4848
/// </summary>
4949
public int Count { get; set; }
50+
51+
52+
/// <inheritdoc/>
53+
public override string ToString()
54+
{
55+
return Value is null ? TableViewLocalizedStrings.BlankFilterValue : $"{Value}";
56+
}
5057
}

src/TableViewColumnHeader.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.UI.Xaml.Media.Imaging;
88
using Microsoft.UI.Xaml.Shapes;
99
using System;
10+
using System.Collections.Generic;
1011
using System.Linq;
1112
using System.Threading.Tasks;
1213
using Windows.System;
@@ -161,12 +162,28 @@ private void ApplyFilter()
161162
}
162163
else if (_tableView is not null)
163164
{
164-
_optionsFlyoutViewModel.SelectedValues = [.. _optionsFlyoutViewModel.FilterItems.Where(x => x.IsSelected).Select(x => x.Value)];
165-
{
165+
_optionsFlyoutViewModel.SelectedValues = GetSelectedValues();
166166
_tableView.FilterHandler.SelectedValues[Column!] = _optionsFlyoutViewModel.SelectedValues;
167167
_tableView.FilterHandler?.ApplyFilter(Column!);
168168
}
169169
}
170+
171+
private ICollection<object?> GetSelectedValues()
172+
{
173+
var selectedValues = _optionsFlyoutViewModel.FilterItems.Where(x => x.IsSelected).Select(x => x.Value);
174+
var firstItem = selectedValues.FirstOrDefault(x => x is not null);
175+
var firstItemType = firstItem?.GetType();
176+
177+
return firstItemType switch
178+
{
179+
Type t when t == typeof(int) => new ObjectBackedTypedSet<int?>(selectedValues),
180+
Type t when t == typeof(DateTime) => new ObjectBackedTypedSet<DateTime?>(selectedValues),
181+
Type t when t == typeof(bool) => new ObjectBackedTypedSet<bool?>(selectedValues),
182+
Type t when t == typeof(long) => new ObjectBackedTypedSet<long?>(selectedValues),
183+
Type t when t == typeof(double) => new ObjectBackedTypedSet<double?>(selectedValues),
184+
185+
_ => [.. selectedValues],
186+
};
170187
}
171188

172189
/// <summary>

0 commit comments

Comments
 (0)