Skip to content

Conversation

@w-ahmad
Copy link
Owner

@w-ahmad w-ahmad commented Dec 2, 2025

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 is False. 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

<tv:TableView ShowFilterItemsCount="True" ItemsSource="{Binding Items}"/>
image

Copy link
Contributor

Copilot AI left a 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 ShowFilterItemsCount boolean property to TableView with a default value of false
  • Added Count property to TableViewFilterItem to 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:" />
Copy link

Copilot AI Dec 3, 2025

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.

Suggested change
<Run Text="Count:" />
<Run Text="{x:Bind local:TableViewLocalizedStrings.FilterItemCountLabel, Mode=OneWay}" />

Copilot uses AI. Check for mistakes.
Comment on lines +33 to +51
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)];
Copy link

Copilot AI Dec 3, 2025

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.

Suggested change
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))];

Copilot uses AI. Check for mistakes.
DetachPropertyChangedHandlers();
_filterItems = value;
AttachPropertyChangedHandlers();
AttachPropertyChangedHandlers();
Copy link

Copilot AI Dec 3, 2025

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.

Suggested change
AttachPropertyChangedHandlers();
AttachPropertyChangedHandlers();

Copilot uses AI. Check for mistakes.
/// <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));
Copy link

Copilot AI Dec 3, 2025

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(...)
Suggested change
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));

Copilot uses AI. Check for mistakes.
TextWrapping="NoWrap" />

<TextBlock Grid.Column="1"
Visibility="{Binding DataContext.TableView.ShowFilterItemCounts, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}">
Copy link

Copilot AI Dec 3, 2025

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.

Suggested change
Visibility="{Binding DataContext.TableView.ShowFilterItemCounts, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}">
Visibility="{Binding DataContext.TableView.ShowFilterItemsCount, ElementName=FilterItemsList, Converter={StaticResource BoolToVisibility}}">

Copilot uses AI. Check for mistakes.
(column.IsFiltered && SelectedValues[column].Contains(value));

return string.IsNullOrEmpty(searchText)
|| value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true
Copy link

Copilot AI Dec 3, 2025

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'.

Suggested change
|| value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase) == true
|| value?.ToString()?.Contains(searchText, StringComparison.OrdinalIgnoreCase)

Copilot uses AI. Check for mistakes.
@w-ahmad w-ahmad merged commit ffc18de into main Dec 12, 2025
1 check passed
@w-ahmad w-ahmad deleted the feat/filter_items_count branch December 12, 2025 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

About filter

2 participants