Skip to content

Add Excel/DataGrip-style distinct-value column filtering#318

Open
wieslawsoltes wants to merge 4 commits into
masterfrom
agent/issue-301-distinct-value-filter
Open

Add Excel/DataGrip-style distinct-value column filtering#318
wieslawsoltes wants to merge 4 commits into
masterfrom
agent/issue-301-distinct-value-filter

Conversation

@wieslawsoltes

@wieslawsoltes wieslawsoltes commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the Excel/DataGrip-style distinct-value column filter requested in #301.

The new DataGridDistinctValueFilterFlyout opens from the existing column-header filter button and provides:

  • a compiled-binding ListBox whose rows contain a checkbox, formatted value, and source-row count;
  • case-insensitive substring search over displayed values;
  • unchecked-by-default behavior when the column has no active filter;
  • immediate FilteringOperator.In descriptor updates when one or more values are checked;
  • removal of the column descriptor when the final checked value is unchecked;
  • selection restoration when the popup is reopened;
  • configurable value formatting and equality semantics;
  • reflection-free value reads through IDataGridColumnValueAccessor.

Fixes #301.

Previous gap

ProDataGrid already had the pieces needed to apply an In descriptor and display an arbitrary per-column FilterFlyout, but it did not provide a reusable distinct-value model or a built-in value/count/search presentation. Each application therefore had to enumerate values, keep checkbox state synchronized with FilteringModel, and build its own popup.

Design

One filtering pipeline

DataGridDistinctValueFilterContext owns only popup state: distinct options, counts, search text, and checkbox selection. It does not introduce another filtering pipeline. Checkbox changes call the existing central IFilteringModel.SetOrUpdate/Remove APIs, so header filtered state, selection preservation, adapter ownership, and collection-view refresh behavior remain unchanged.

The generated descriptor uses:

  • the column's stable ColumnKey/definition/column identity;
  • FilteringOperator.In;
  • the column's resolved sort/binding property path when available;
  • the checked raw values.

When a custom ValueComparer is supplied, the descriptor also carries a typed predicate so grouping and row-membership equality remain consistent.

Reflection-free value access

The popup resolves values in this order:

  1. DataGridDistinctValueFilterFlyout.ValueAccessor;
  2. DataGridColumnFilter.ValueAccessor;
  3. the accessor generated by a typed DataGridColumnDefinition binding.

It deliberately does not reflect over the binding or SortMemberPath. If no accessor is available, opening is canceled and LastError explains the requirement. This keeps the feature trimming/AOT friendly and follows the repository's no-new-reflection rule.

Counts and reopening

When the grid is backed by IDataGridCollectionView, the popup enumerates SourceCollection, not the currently filtered view. Reopening a filter therefore still shows every source value and its full count even when that same column currently filters the visible rows.

Null values are grouped and displayed as (Empty). Options are sorted using the current culture, case-insensitively.

Search and selection semantics

Search only changes the visible Options collection. It does not clear selections hidden by the current search. Rebuilding the list restores checked state from the active In descriptor without reapplying the filter.

Theme and accessibility

Adds the compiled-binding DataGridFilterDistinctValuesEditorTemplate to Themes/Generic.xaml, with:

  • DataGridFilterDistinctValuesEditorWidth;
  • DataGridFilterDistinctValuesListMaxHeight;
  • accessible names for the search box, values list, and individual checkboxes;
  • coverage in both Simple and Fluent theme resource tests, including v2 variants.

Sample and documentation

  • Updates the Column Definitions + FilteringModel sample so the Status header uses the new built-in popup with its existing typed binding accessor.
  • Adds a dedicated Distinct-Value Column Filtering guide covering typed definitions, existing-column accessors, customization, equality, formatting, and template replacement.
  • Links the guide from Data Operations, Column Header Menus and Filters, and the DocFX table of contents.

Tests

New model tests cover:

  • sorted distinct values and counts, including null;
  • unchecked defaults;
  • case-insensitive substring search;
  • selection persistence while search hides values;
  • In descriptor creation and removal;
  • selection restoration from an existing descriptor;
  • custom equality grouping and predicate consistency.

Headless integration tests cover:

  • opening from a real column header;
  • template resolution;
  • source counts and immediate collection-view filtering;
  • clearing by unchecking the final value;
  • rebuilding from the unfiltered source on reopen;
  • flyout-level accessor/comparer/formatter overrides;
  • graceful cancellation when no typed accessor exists.

Validation

  • dotnet test ... -f net10.0: 2,259 passed, 0 failed
  • focused distinct-value/flyout/theme tests: 30 passed, 0 failed
  • production library build (net8.0 and net10.0): 0 errors
  • DataGrid sample build (net10.0): 0 errors
  • DocFX build: 0 errors (existing repository warnings remain)
  • git diff --check: clean

Builds retain the repository's existing warning baseline; this change adds no build errors.

Review hardening

Follow-up review preserves filter identity and state across ambiguous or changing data:

  • exact descriptor identity is preferred before falling back to ColumnId matching;
  • selected values temporarily absent from the current distinct-value scan remain available as zero-count options, including comparer and null semantics.

Current validation: 8/8 focused distinct-filter tests and 2,262/2,262 full unit tests pass. Review threads are resolved.

@wieslawsoltes

Copy link
Copy Markdown
Owner Author

Self-review completed. I found and fixed descriptor restoration ambiguity for custom IFilteringModel implementations containing multiple descriptors with the same property path: exact column identity now takes precedence over property-path fallback. Commit 9dc052b adds regression coverage. Focused tests passed 6/6, the full unit suite passed 2,260/2,260, all six CI checks are successful, and git diff --check is clean.

@wieslawsoltes
wieslawsoltes marked this pull request as ready for review July 15, 2026 00:25

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9dc052ba34

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Avalonia.Controls.DataGrid/Filtering/DataGridDistinctValueFilterContext.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3fba1aa44

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Excel/DataGrip-Style Column Filtering

1 participant