-
-
Notifications
You must be signed in to change notification settings - Fork 42
feat: Added filter item count to the FilterFlyout #262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds the ability to display item counts next to filter items in the column header filter flyout. Users can enable this feature by setting ShowFilterItemsCount="True" on the TableView. When enabled, filter items are sorted by count in descending order; otherwise they maintain alphabetical ordering.
Key changes:
- Added
ShowFilterItemsCountboolean property to TableView with a default value offalse - Added
Countproperty toTableViewFilterItemto store the number of occurrences - Refactored
ColumnFilterHandler.GetFilterItems()to use GroupBy for counting instead of SortedSet for deduplication
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Themes/TableViewColumnHeader.xaml | Updated filter flyout UI to display count next to each filter item using a two-column grid layout with conditional visibility |
| src/TableViewColumnHeader.cs | Refactored template application to use event-based loading and added scroll-to-top behavior when filter flyout opens |
| src/TableViewColumnHeader.OptionsFlyoutViewModel.cs | Changed visibility from private to internal to support XAML binding, and made SetSelectAllCheckBoxState() internal |
| src/TableViewColumnHeader.FilterItem.cs | Added Count property and updated constructor to accept optional count parameter |
| src/TableView.Properties.cs | Added ShowFilterItemsCount dependency property with XML documentation |
| src/ColumnFilterHandler.cs | Refactored filtering logic to use LINQ GroupBy for counting, replaced SortedSet with GroupBy/OrderBy chain |
|
|
||
| <TextBlock Grid.Column="1" | ||
| Visibility="{Binding DataContext.TableView.ShowFilterItemCounts, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}"> | ||
| <Run Text="Count:" /> |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hardcoded "Count:" text should be localized. Add this string to the localization resources (src/Strings/{locale}/WinUI.TableView.resw) and use TableViewLocalizedStrings class to access it, similar to how other strings like SelectAllParenthesized and BlankFilterValue are handled. This ensures proper internationalization support for all supported languages.
| <Run Text="Count:" /> | |
| <Run Text="{x:Bind local:TableViewLocalizedStrings.FilterItemCountLabel, Mode=OneWay}" /> |
| return [.. collectionView.Select(column.GetCellContent) | ||
| .Select(x => IsBlank(x) ? null : x) | ||
| .GroupBy(x => x) | ||
| .OrderBy(x => x.Key) | ||
| .Select(x => | ||
| { | ||
| var value = x.Key; | ||
| value ??= TableViewLocalizedStrings.BlankFilterValue; | ||
| var isSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) || | ||
| (column.IsFiltered && SelectedValues[column].Contains(value)); | ||
|
|
||
| return string.IsNullOrEmpty(searchText) | ||
| || value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true | ||
| ? new TableViewFilterItem(isSelected, value, x.Count()) | ||
| : null; | ||
|
|
||
| }) | ||
| .OfType<TableViewFilterItem>() | ||
| .OrderByDescending(x => _tableView.ShowFilterItemsCount ? x.Count : 0)]; |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance concern: The LINQ chain now has two separate sorting operations - .OrderBy(x => x.Key) on line 36 followed by .OrderByDescending(x => _tableView.ShowFilterItemsCount ? x.Count : 0) on line 51. When ShowFilterItemsCount is false, the second OrderBy still executes but sorts by 0 (no-op sort). Consider optimizing by applying conditional ordering:
var items = collectionView.Select(column.GetCellContent)
.Select(x => IsBlank(x) ? null : x)
.GroupBy(x => x)
.Select(x => { /* create filter item */ })
.OfType<TableViewFilterItem>();
return [.. (_tableView.ShowFilterItemsCount
? items.OrderByDescending(x => x.Count)
: items.OrderBy(x => x.Value))];This avoids the unnecessary second sort and maintains the original alphabetical ordering when count display is disabled.
| return [.. collectionView.Select(column.GetCellContent) | |
| .Select(x => IsBlank(x) ? null : x) | |
| .GroupBy(x => x) | |
| .OrderBy(x => x.Key) | |
| .Select(x => | |
| { | |
| var value = x.Key; | |
| value ??= TableViewLocalizedStrings.BlankFilterValue; | |
| var isSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) || | |
| (column.IsFiltered && SelectedValues[column].Contains(value)); | |
| return string.IsNullOrEmpty(searchText) | |
| || value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true | |
| ? new TableViewFilterItem(isSelected, value, x.Count()) | |
| : null; | |
| }) | |
| .OfType<TableViewFilterItem>() | |
| .OrderByDescending(x => _tableView.ShowFilterItemsCount ? x.Count : 0)]; | |
| var items = collectionView.Select(column.GetCellContent) | |
| .Select(x => IsBlank(x) ? null : x) | |
| .GroupBy(x => x) | |
| .Select(x => | |
| { | |
| var value = x.Key; | |
| value ??= TableViewLocalizedStrings.BlankFilterValue; | |
| var isSelected = !column.IsFiltered || !string.IsNullOrEmpty(searchText) || | |
| (column.IsFiltered && SelectedValues[column].Contains(value)); | |
| return string.IsNullOrEmpty(searchText) | |
| || value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true | |
| ? new TableViewFilterItem(isSelected, value, x.Count()) | |
| : null; | |
| }) | |
| .OfType<TableViewFilterItem>(); | |
| return [.. (_tableView.ShowFilterItemsCount | |
| ? items.OrderByDescending(x => x.Count) | |
| : items.OrderBy(x => x.Value))]; |
| DetachPropertyChangedHandlers(); | ||
| _filterItems = value; | ||
| AttachPropertyChangedHandlers(); | ||
| AttachPropertyChangedHandlers(); |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing whitespace detected after the semicolon. Remove the extra spaces at the end of the line to maintain consistent code formatting.
| AttachPropertyChangedHandlers(); | |
| AttachPropertyChangedHandlers(); |
| /// <summary> | ||
| /// Identifies the <see cref="ShowFilterItemsCount"/> dependency property. | ||
| /// </summary> | ||
| public static readonly DependencyProperty ShowFilterItemsCountProperty = DependencyProperty.Register(nameof(ShowFilterItemsCount), typeof(bool), typeof(TableView), new PropertyMetadata(false)); |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent spacing: there are two spaces after the = sign instead of one. For consistency with other DependencyProperty declarations in this file (lines 22-261), use a single space:
public static readonly DependencyProperty ShowFilterItemsCountProperty = DependencyProperty.Register(...)| public static readonly DependencyProperty ShowFilterItemsCountProperty = DependencyProperty.Register(nameof(ShowFilterItemsCount), typeof(bool), typeof(TableView), new PropertyMetadata(false)); | |
| public static readonly DependencyProperty ShowFilterItemsCountProperty = DependencyProperty.Register(nameof(ShowFilterItemsCount), typeof(bool), typeof(TableView), new PropertyMetadata(false)); |
| TextWrapping="NoWrap" /> | ||
|
|
||
| <TextBlock Grid.Column="1" | ||
| Visibility="{Binding DataContext.TableView.ShowFilterItemCounts, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}"> |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The binding path is incorrect. The property name is ShowFilterItemsCount (not ShowFilterItemCounts). This typo will cause the binding to fail and the count visibility will not work correctly. Change ShowFilterItemCounts to ShowFilterItemsCount.
| Visibility="{Binding DataContext.TableView.ShowFilterItemCounts, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}"> | |
| Visibility="{Binding DataContext.TableView.ShowFilterItemsCount, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}"> |
| (column.IsFiltered && SelectedValues[column].Contains(value)); | ||
|
|
||
| return string.IsNullOrEmpty(searchText) | ||
| || value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expression 'A == true' can be simplified to 'A'.
| || value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true | |
| || value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) |
closes #76
PR Summary
This PR introduces the ability to display item counts in the filter flyout and refactors the filtering logic for better performance and maintainability.
TableView.ShowFilterItemsCount: Property to toggle items count in filter flyout. The default value isFalse. When this property set to true, the items in flyout would be sorted by the count in descending order.TableViewFilterItem.Count: Property to indicate items count in filter flyout. This property can be set through the constructor or direct assignment.Example