Skip to content

Commit ffc18de

Browse files
authored
Merge pull request #262 from w-ahmad/feat/filter_items_count
feat: Added filter item count to the FilterFlyout
2 parents c579106 + a8dabc9 commit ffc18de

File tree

6 files changed

+102
-41
lines changed

6 files changed

+102
-41
lines changed

src/ColumnFilterHandler.cs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,25 @@ public virtual IList<TableViewFilterItem> GetFilterItems(TableViewColumn column,
3030
column.TableView.FilterDescriptions.Where(
3131
x => x is not ColumnFilterDescription columnFilter || columnFilter.Column != column));
3232

33-
var filterValues = new SortedSet<object?>();
34-
35-
foreach (var item in collectionView)
36-
{
37-
var value = column.GetCellContent(item);
38-
filterValues.Add(IsBlank(value) ? null : value);
39-
}
40-
41-
return [.. filterValues.Select(value =>
42-
{
43-
value ??= TableViewLocalizedStrings.BlankFilterValue;
44-
var isSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) ||
45-
(column.IsFiltered && SelectedValues[column].Contains(value));
46-
47-
return string.IsNullOrEmpty(searchText)
48-
|| value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true
49-
? new TableViewFilterItem(isSelected, value)
50-
: null;
51-
52-
}).OfType<TableViewFilterItem>()];
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)];
5352
}
5453

5554
return [];

src/TableView.Properties.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,11 @@ public partial class TableView
260260
/// </summary>
261261
public static readonly DependencyProperty ConditionalCellStylesProperty = DependencyProperty.Register(nameof(ConditionalCellStyles), typeof(IList<TableViewConditionalCellStyle>), typeof(TableView), new PropertyMetadata(default));
262262

263+
/// <summary>
264+
/// Identifies the <see cref="ShowFilterItemsCount"/> dependency property.
265+
/// </summary>
266+
public static readonly DependencyProperty ShowFilterItemsCountProperty = DependencyProperty.Register(nameof(ShowFilterItemsCount), typeof(bool), typeof(TableView), new PropertyMetadata(false));
267+
263268
/// <summary>
264269
/// Gets or sets a value indicating whether opening the column filter over header right-click is enabled.
265270
/// </summary>
@@ -316,6 +321,15 @@ public IList<TableViewConditionalCellStyle> ConditionalCellStyles
316321
set => SetValue(ConditionalCellStylesProperty, value);
317322
}
318323

324+
/// <summary>
325+
/// Gets or sets a value that indicates whether the TableView displays items count next to each filter item in filter flyout.
326+
/// </summary>
327+
public bool ShowFilterItemsCount
328+
{
329+
get => (bool)GetValue(ShowFilterItemsCountProperty);
330+
set => SetValue(ShowFilterItemsCountProperty, value);
331+
}
332+
319333
/// <summary>
320334
/// Gets or sets the selection start cell slot.
321335
/// </summary>

src/TableViewColumnHeader.FilterItem.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace WinUI.TableView;
55
/// <summary>
66
/// Represents a filter item used in the options flyout of a TableViewColumnHeader.
77
/// </summary>
8-
public partial class TableViewFilterItem : INotifyPropertyChanged
8+
public partial class TableViewFilterItem : INotifyPropertyChanged
99
{
1010
/// <inheritdoc/>
1111
public event PropertyChangedEventHandler? PropertyChanged;
@@ -17,10 +17,12 @@ public partial class TableViewFilterItem : INotifyPropertyChanged
1717
/// </summary>
1818
/// <param name="isSelected">Indicates whether the filter item is selected.</param>
1919
/// <param name="value">The value of the filter item.</param>
20-
public TableViewFilterItem(bool isSelected, object value)
20+
/// <param name="count">The count of occurrences for the filter item.</param>
21+
public TableViewFilterItem(bool isSelected, object value, int count = 1)
2122
{
2223
IsSelected = isSelected;
2324
Value = value;
25+
Count = count;
2426
}
2527

2628
/// <summary>
@@ -40,4 +42,9 @@ public bool IsSelected
4042
/// Gets the value of the filter item.
4143
/// </summary>
4244
public object Value { get; }
45+
46+
/// <summary>
47+
/// Gets or sets the count of occurrences for the filter item.
48+
/// </summary>
49+
public int Count { get; set; }
4350
}

src/TableViewColumnHeader.OptionsFlyoutViewModel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public partial class TableViewColumnHeader
1212
/// <summary>
1313
/// ViewModel for the options flyout in the TableViewColumnHeader.
1414
/// </summary>
15-
private partial class OptionsFlyoutViewModel : INotifyPropertyChanged
15+
internal partial class OptionsFlyoutViewModel : INotifyPropertyChanged
1616
{
1717
public event PropertyChangedEventHandler? PropertyChanged;
1818
private IList<TableViewFilterItem> _filterItems = [];
@@ -114,7 +114,7 @@ public IList<TableViewFilterItem> FilterItems
114114

115115
DetachPropertyChangedHandlers();
116116
_filterItems = value;
117-
AttachPropertyChangedHandlers();
117+
AttachPropertyChangedHandlers();
118118
SetSelectAllCheckBoxState();
119119
OnPropertyChanged();
120120
}
@@ -128,7 +128,7 @@ public IList<TableViewFilterItem> FilterItems
128128
/// <summary>
129129
/// Sets the state of the select all checkbox.
130130
/// </summary>
131-
private void SetSelectAllCheckBoxState()
131+
internal void SetSelectAllCheckBoxState()
132132
{
133133
if (ColumnHeader._selectAllCheckBox is null || !_canSetState)
134134
{

src/TableViewColumnHeader.cs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public partial class TableViewColumnHeader : ContentControl
4646
private double _reorderStartingPosition;
4747
private bool _reorderStarted;
4848
private RenderTargetBitmap? _dragVisuals;
49+
private ListView? _filterItemsList;
4950

5051
/// <summary>
5152
/// Initializes a new instance of the TableViewColumnHeader class.
@@ -228,24 +229,33 @@ protected override void OnApplyTemplate()
228229
_optionsButton.Tapped += OnOptionsButtonTaped;
229230
_optionsButton.DataContext = _optionsFlyoutViewModel = new OptionsFlyoutViewModel(_tableView, this);
230231

231-
var menuItem = _optionsFlyout.Items.FirstOrDefault(x => x.Name == "ItemsCheckFlyoutItem");
232-
menuItem?.ApplyTemplate();
232+
if (_optionsFlyout.Items.FirstOrDefault(x => x.Name == "ItemsCheckFlyoutItem") is { } menuItem)
233+
{
234+
menuItem.Loaded += OnItemsCheckFlyoutItemLoaded;
235+
}
236+
237+
SetFilterButtonVisibility();
238+
EnsureGridLines();
239+
}
240+
241+
/// <summary>
242+
/// Handles the Loaded event for the items check flyout item.
243+
/// </summary>
244+
private void OnItemsCheckFlyoutItemLoaded(object sender, RoutedEventArgs e)
245+
{
246+
if (sender is not MenuFlyoutItem menuItem) return;
247+
248+
menuItem.Loaded -= OnItemsCheckFlyoutItemLoaded;
233249

234250
if (menuItem?.FindDescendant<CheckBox>(x => x.Name == "SelectAllCheckBox") is { } checkBox)
235251
{
236252
_selectAllCheckBox = checkBox;
237253
_selectAllCheckBox.Content = TableViewLocalizedStrings.SelectAllParenthesized;
238254
_selectAllCheckBox.Checked += OnSelectAllCheckBoxChecked;
239255
_selectAllCheckBox.Unchecked += OnSelectAllCheckBoxUnchecked;
256+
_optionsFlyoutViewModel.SetSelectAllCheckBoxState();
240257
}
241258

242-
#if !WINDOWS
243-
if (menuItem?.FindDescendant<ListView>(x => x.Name is "FilterItemsList") is { } filterItemsList)
244-
{
245-
filterItemsList.Margin = new Thickness(12, 0, 0, 0);
246-
}
247-
#endif
248-
249259
if (menuItem?.FindDescendant<TextBox>(x => x.Name == "SearchBox") is { } searchBox)
250260
{
251261
_searchBox = searchBox;
@@ -260,8 +270,7 @@ protected override void OnApplyTemplate()
260270
menuItem.PreviewKeyUp += static (_, e) => e.Handled = e.Key is VirtualKey.Space;
261271
}
262272

263-
SetFilterButtonVisibility();
264-
EnsureGridLines();
273+
_filterItemsList = menuItem?.FindDescendant<ListView>(x => x.Name is "FilterItemsList");
265274
}
266275

267276
/// <summary>
@@ -321,6 +330,11 @@ private async void OnOptionsFlyoutOpening(object? sender, object e)
321330
await Task.Delay(100);
322331
await FocusManager.TryFocusAsync(_searchBox, FocusState.Programmatic);
323332
}
333+
334+
if (_filterItemsList is not null && _optionsFlyoutViewModel.FilterItems.Count > 0)
335+
{
336+
_filterItemsList.ScrollIntoView(_optionsFlyoutViewModel.FilterItems[0]);
337+
}
324338
}
325339

326340
/// <summary>

src/Themes/TableViewColumnHeader.xaml

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
22
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
34
xmlns:local="using:WinUI.TableView"
45
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
5-
xmlns:not_win="http://uno.ui/not_win">
6+
xmlns:not_win="http://uno.ui/not_win"
7+
xmlns:converters="using:WinUI.TableView.Converters"
8+
mc:Ignorable="not_win">
69

710
<ResourceDictionary.MergedDictionaries>
811
<ResourceDictionary Source="ms-appx:///WinUI.TableView/Themes/Resources.xaml" />
912
</ResourceDictionary.MergedDictionaries>
1013

14+
<converters:BoolToVisibilityConverter x:Key="BoolToVisibility" />
15+
1116
<Style x:Key="DefaultTableViewColumnHeaderStyle"
1217
TargetType="local:TableViewColumnHeader">
1318
<Setter Property="FontWeight"
@@ -150,7 +155,7 @@
150155
<MenuFlyoutItem.Template>
151156
<ControlTemplate TargetType="MenuFlyoutItem">
152157
<Grid Margin="8,4"
153-
Width="220"
158+
Width="250"
154159
Height="300">
155160
<Grid.RowDefinitions>
156161
<RowDefinition Height="Auto" />
@@ -167,6 +172,7 @@
167172
<ListView x:Name="FilterItemsList"
168173
Grid.Row="2"
169174
SelectionMode="None"
175+
not_win:Margin="12,0,0,0"
170176
ItemsSource="{Binding FilterItems}">
171177
<ListView.ItemContainerStyle>
172178
<Style TargetType="ListViewItem"
@@ -179,13 +185,34 @@
179185
Value="36" />
180186
<Setter Property="Height"
181187
Value="36" />
188+
<Setter Property="HorizontalAlignment"
189+
Value="Stretch" />
190+
<Setter Property="HorizontalContentAlignment"
191+
Value="Stretch" />
182192
</Style>
183193
</ListView.ItemContainerStyle>
184194
<ListView.ItemTemplate>
185195
<DataTemplate>
186-
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}">
187-
<TextBlock Text="{Binding Value}"
188-
TextWrapping="NoWrap" />
196+
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
197+
HorizontalAlignment="Stretch"
198+
HorizontalContentAlignment="Stretch">
199+
<Grid>
200+
<Grid.ColumnDefinitions>
201+
<ColumnDefinition Width="*" />
202+
<ColumnDefinition Width="Auto" />
203+
</Grid.ColumnDefinitions>
204+
205+
<TextBlock Text="{Binding Value}"
206+
TextWrapping="NoWrap" />
207+
208+
<StackPanel Grid.Column="1"
209+
Orientation="Horizontal"
210+
Visibility="{Binding DataContext.TableView.ShowFilterItemsCount, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}">
211+
<TextBlock Text="(" />
212+
<TextBlock Text="{Binding Count}" />
213+
<TextBlock Text=")" />
214+
</StackPanel>
215+
</Grid>
189216
</CheckBox>
190217
</DataTemplate>
191218
</ListView.ItemTemplate>

0 commit comments

Comments
 (0)