dotnet/wpf · error · InvalidOperationException
SR.DataGrid_CannotSelectCell
Error message
SR.DataGrid_CannotSelectCell
What it means
DataGrid throws this InvalidOperationException when cells are added to or removed from the SelectedCells collection while SelectionUnit is DataGridSelectionUnit.FullRow. In FullRow mode only entire rows can be selected, so individual cell selection state changes are illegal. The check is skipped while the DataGrid itself is updating SelectedCells internally (IsUpdatingSelectedCells).
Solutions
- Change SelectionUnit to DataGridSelectionUnit.CellOrRowHeader (or Cell) if cell selection is required
- Replace cell-selection calls with row selection (set DataGridRow.IsSelected or use SelectedItems)
- Wrap cell-selection mutation in a check of SelectionUnit before calling
- Use SetSelectedCellsToBounds/DataGrid APIs rather than editing SelectedCells directly
Example fix
// before
if (grid.SelectedCells.Count == 0) grid.SelectedCells.Add(new DataGridCellInfo(grid.Items[0]));
// after
if (grid.SelectionUnit != DataGridSelectionUnit.FullRow && grid.SelectedCells.Count == 0)
grid.SelectedCells.Add(new DataGridCellInfo(grid.Items[0]));
else
grid.SelectedIndex = 0; Defensive patterns
Strategy: validation
Validate before calling
if (grid.SelectionUnit == DataGridSelectionUnit.FullRow)
throw new InvalidOperationException("Cell selection is not allowed in FullRow selection mode."); Type guard
bool CanSelectCells(DataGrid g) => g.SelectionUnit != DataGridSelectionUnit.FullRow;
Try / catch
try { grid.SelectedCells.Add(cellInfo); }
catch (InvalidOperationException ex) when (ex.Message.Contains("select")) { grid.SelectedIndex = cellInfo.Item is DataGridRow r ? r.GetIndex() : grid.Items.IndexOf(cellInfo.Item); } Prevention
- Match selection code to SelectionUnit configuration
- Prefer SelectedItems/row-level APIs in FullRow mode
- Revisit selection code whenever SelectionUnit changes in XAML
When it happens
Trigger: Directly mutating DataGrid.SelectedCells (Add/Remove/Clear) via OnSelectedCellsChanged or code-behind when SelectionMode/SelectionUnit is configured as FullRow; calling selection APIs on the collection instead of selecting rows.
Common situations: Developers switching a grid from CellOrRowHeader to FullRow selection but keeping old cell-selection code; custom selection logic written for cell-selection mode reused after changing SelectionUnit in XAML.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- SR.DataGridRow_CannotSelectRowWhenCells
- Can only call SelectAll when CanSelectMultipleItems is true.
- SR.DataGrid_CannotSelectCell
- SR.DataGridRow_CannotSelectRowWhenCells
- SR.DeferSelectionNotActive
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/29dd90d4eb9e8843.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/DataGrid.cs:4112
{
get { return _selectedCells; }
}
/// <summary>
/// Event that fires when the SelectedCells collection changes.
/// </summary>
public event SelectedCellsChangedEventHandler SelectedCellsChanged;
/// <summary>
/// Direct notification from the SelectedCells collection of a change.
/// </summary>
internal void OnSelectedCellsChanged(NotifyCollectionChangedAction action, VirtualizedCellInfoCollection oldItems, VirtualizedCellInfoCollection newItems)
{
DataGridSelectionMode selectionMode = SelectionMode;
DataGridSelectionUnit selectionUnit = SelectionUnit;
if (!IsUpdatingSelectedCells && (selectionUnit == DataGridSelectionUnit.FullRow))
{
throw new InvalidOperationException(SR.DataGrid_CannotSelectCell);
}
// Update the pending list of changes
if (oldItems != null)
{
// When IsUpdatingSelectedCells is true, there may have been cells
// added to _pendingSelectedCells that are now being removed.
// These cells should be removed from _pendingSelectedCells and
// not added to _pendingUnselectedCells.
if (_pendingSelectedCells != null)
{
VirtualizedCellInfoCollection.Xor(_pendingSelectedCells, oldItems);
}
if (_pendingUnselectedCells == null)
{
_pendingUnselectedCells = oldItems;
}View on GitHub (pinned to 81131a70a4)