Skip to content

[DataGrid] Add Visible parameter to hide columns without losing their state - #5333

Draft
sebguischr wants to merge 2 commits into
microsoft:devfrom
sebguischr:feature/datagrid-column-visibility
Draft

sebguischr wants to merge 2 commits into
microsoft:devfrom
sebguischr:feature/datagrid-column-visibility

Conversation

@sebguischr

@sebguischr sebguischr commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Pull Request

📖 Description

Adds a Visible parameter to DataGrid columns, so that a column can be hidden (for instance from a column chooser) without losing its state.

Today the only way to hide a column is to leave it out with @if (see the "Dynamic columns" demo). The column is then removed from the grid and comes back as a new column, with two side effects (verified with bUnit on dev):

  • it comes back after all the other columns, whatever its declared place or the place the user had moved it to;
  • if the grid was sorted by it, the grid keeps a sort level on the removed instance, so the header of the column that comes back no longer shows the sort (aria-sort="none" everywhere).

With Visible="false" the column stays part of the grid:

  • the data stays sorted by it, and its header shows the sort again once it is displayed;
  • it keeps its place in the column order (GetColumnOrder(), ColumnOrder, ColumnOrderChanged still list it), even when the displayed columns are moved in the meantime;
  • SortByColumnAsync(title) / AddSortByColumnAsync(title) and the sort restored from the URL (SaveStateInUrl) still find it.
<FluentCheckbox @bind-Value="showDepartment" Label="Department" />

<FluentDataGrid Items="@people" ReorderableColumns="true">
    <PropertyColumn Property="@(p => p.LastName)" Sortable="true" />
    <PropertyColumn Property="@(p => p.Department)" Sortable="true" Visible="@showDepartment" />
</FluentDataGrid>

Implementation

  • _columns still holds every collected column in display order, hidden ones included, so the column order, the column keys and the sort are unchanged.
  • The displayed columns come from an internal VisibleColumns property, which returns _columns itself when no column is hidden (the usual case allocates nothing), and the filtered list otherwise. Rendering (rows, headers, placeholders, colspans), GridTemplateColumns, pinned offsets, the resize/reorder setup, FluentDataGridCell.Column and FluentDataGridRow.Columns read the displayed columns.
  • A column's Index is its place among the displayed columns (hidden columns get 0). FluentDataGridCell.Column finds its column by that index, in O(1) when no column is hidden.
  • Moving a displayed column (menu, keyboard or drag and drop) writes it back into the places of the displayed unpinned columns, so pinned and hidden columns stay where they are.
  • When GridTemplateColumns has exactly one track per column, the tracks of hidden columns are removed (spaces inside minmax(...) are handled). Otherwise (e.g. repeat()), it is used as is.

Not a breaking change: Visible defaults to true.

🎫 Issues

None found.

👩‍💻 Reviewer Notes

Two questions:

  1. Should a sort level on a hidden column stay active (as done here, like most data grids), or be dropped when the column is hidden?
  2. This PR only adds the parameter, so apps build their own column chooser by composition (as in the demo). Would you welcome a follow-up adding a "Hide column" item to the header menu plus a small FluentDataGridColumnChooser component (that would also need VisibleChanged)?

Smoke test: demo site, /DataGrid/DynamicColumns, new "Hide columns" section: sort by "Department" (or move a column), uncheck "Department", check it again → it comes back at its place, still sorted.

📑 Test Plan

New FluentDataGridColumnVisibilityTests (10 tests):

  • a hidden column renders no header/cells and no grid track;
  • hiding the sorted column keeps the sort (same column instance) and the header shows it again once displayed;
  • a hidden column can still be sorted by title;
  • moving a column while another is hidden keeps the hidden column's slot, and it comes back there;
  • ColumnOrder / GetColumnOrder() hold the hidden column;
  • GridTemplateColumns loses the hidden column's track (minmax(...)), or is left as is with repeat();
  • the RowDetails toggle moves to the first displayed column;
  • a hidden start-pinned column does not offset the next pinned one;
  • without hidden columns, VisibleColumns is _columns itself.

All 459 DataGrid tests pass. Checked manually in the demo (Chrome).

✅ Checklist

General

  • I have added tests for my changes.
  • I have tested my changes.
  • I have updated the project documentation to reflect my changes.
  • I have read the CONTRIBUTING documentation and followed the standards for this project.

Component-specific

  • I have added a new component
  • I have added Unit Tests for my new component
  • I have modified an existing component
  • I have validated the Unit Tests for an existing component

⏭ Next Steps

Depending on the answer to question 2: a "Hide column" header menu item and a column chooser component.

🤖 Generated with Claude Code

…their state

A column left out with @if is removed from the grid and comes back as a new
column: it is placed after all the other columns, and the grid keeps a sort
level on the removed instance, so the header no longer shows the sort.

ColumnBase.Visible hides a column while keeping it part of the grid. The grid
now tracks every collected column (_allColumns) next to the displayed ones
(_columns): the column keys, the column order (GetColumnOrder, ColumnOrder,
ColumnOrderChanged), the sort by title and the sort restored from the URL use
every column, while rendering, indices, pinned offsets and header UI use the
displayed ones. Moving a displayed column keeps the place of the hidden ones.
The tracks of hidden columns are removed from GridTemplateColumns when it has
one track per column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The implementation is cohesive and well-covered by new tests, with only a minor test naming mismatch to optionally address.

Review effort: Lite
Findings: 1 Low severity

Open (1)
What changed in this PR

This PR adds a Visible parameter to DataGrid columns so columns can be hidden without being removed/recreated, preserving column instance state (sort levels, order slots, keys) while omitted from rendering.

Changes:

  • Introduced ColumnBase.Visible (default true) and updated FluentDataGrid to maintain both “all columns” and “visible columns” collections.
  • Updated ordering/sorting/keying logic to operate over all collected columns while keeping rendering/layout/index-based behaviors driven by visible columns.
  • Added a demo example + documentation and introduced a dedicated bUnit test suite covering visibility behaviors.
File Description
tests/​Core/​Components/​DataGrid/​FluentDataGridColumnVisibilityTests.razor Adds coverage for hidden-column rendering, sorting, ordering, template tracks, row-details toggle placement, and pinned offsets.
src/​Core/​Components/​DataGrid/​FluentDataGrid.razor.cs Implements _allColumns vs _columns split, merges reorders back into _allColumns, and adapts template/pinning/sort resolution accordingly.
src/​Core/​Components/​DataGrid/​Columns/​ColumnBase.razor.cs Adds the new Visible parameter with XML docs describing behavioral differences vs @if.
examples/​Demo/​FluentUI.Demo.Client/​Documentation/​Components/​DataGrid/​Pages/​DataGridDynamicColumnsPage.md Documents the new “Hide columns” approach using Visible.
examples/​Demo/​FluentUI.Demo.Client/​Documentation/​Components/​DataGrid/​Examples/​DataGridColumnVisibility.razor Adds a demo sample showing checkbox-driven column visibility while preserving order/sort.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

}

[Fact]
public void FluentDataGrid_Visible_False_OnAPinnedColumn_OffsetsTheNextPinnedColumnFromTheEdge()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Renamed to FluentDataGrid_Visible_False_OnAPinnedColumn_DoesNotOffsetTheNextPinnedColumn in 3fb04f9.

internal readonly List<ColumnBase<TGridItem>> _columns;
// Every collected column in display order, hidden ones (ColumnBase.Visible) included, so that the column order,
// the sort and the column keys keep holding them while they are hidden.
internal readonly List<ColumnBase<TGridItem>> _allColumns;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think it's dangerous to have two variables, _columns and _allColumns, where one is a subset of the other: a problem with managing one of these variables versus the other can lead to anomalies that are difficult to detect.

Why not use _columns and have a Get property that returns only the visible columns (Visible == true)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed, done in 3fb04f9: there is a single list again.

  • _columns holds every collected column, hidden ones included (so column order, keys and sort are back to the original code);
  • the displayed columns come from an internal VisibleColumns property. It returns _columns itself when no column is hidden, so the usual case does not allocate (covered by a test), and the filtered list otherwise;
  • FluentDataGridCell.Column goes through GetVisibleColumn(index), which is O(1) when no column is hidden, since it is read several times per cell;
  • moving a column now writes it back into the places of the displayed unpinned columns of _columns, so there is no merge between two lists anymore.

The diff is smaller as a result. PR description updated.

…columns

Review feedback: two lists, one a subset of the other, can drift apart.

_columns holds every collected column again, hidden ones included, and the
displayed columns come from the VisibleColumns property, which returns
_columns itself when no column is hidden, so the usual case allocates
nothing. Column order, keys and sort need no change anymore; rendering, the
indices (hidden columns get none), pinned offsets, the grid template, the
resize/reorder setup, FluentDataGridCell.Column (GetVisibleColumn, O(1)
without hidden columns) and FluentDataGridRow.Columns use the displayed
columns. Moving a column writes it back into the places of the displayed
unpinned columns, so pinned and hidden columns stay where they are.

Also renames a test whose name said the opposite of what it checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vnbaaij Vincent Baaij (vnbaaij) changed the title feat(DataGrid): add Visible parameter to hide columns without losing their state Sep 24, 2026
@vnbaaij Vincent Baaij (vnbaaij) modified the milestones: v5.0, v5.0.x Sep 24, 2026
@vnbaaij

Copy link
Copy Markdown
Collaborator

Hi sebguischr,
Thanks for your contribution.

We discussed it and we definitely see value in adding this. But we do want to take some more time to properly analyze the code and solution. We will not merge it in for the final v5 release just yet.

@vnbaaij Vincent Baaij (vnbaaij) added status:needs-investigation Needs additional investigation v5 For the next major version and removed v5 For the next major version labels Sep 24, 2026
@sebguischr

Copy link
Copy Markdown
Contributor Author

Thanks Vincent Baaij (@vnbaaij), understood, no rush on our side.

To give some context on where these PRs come from: we use the DataGrid a lot and would like to help close a few gaps compared to other grids, step by step and in the spirit of the current design (composition rather than built-in features). Column visibility is a first step; next ideas include column footers/summary rows (following up on #273) and a way to save and restore the grid's state (sort, column order, visibility, widths). We'll keep each PR small and focused.

For future proposals, would you prefer that we open a discussion or an issue first, to agree on the design before writing code? Happy to adapt to however you like to work.

@vnbaaij

Copy link
Copy Markdown
Collaborator

Having discussions first woud help, yes. That allows for also getting input from others users/developers than just us.
The composition approach is solid. Happy with how you are approaching this!

We also need to be mindful about maintainability so I can't promise beforehand that all wishes will be granted...

@vnbaaij
Vincent Baaij (vnbaaij) marked this pull request as draft September 25, 2026 09:31

This branch has not been deployed

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

Labels

status:needs-investigation Needs additional investigation

4 participants