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

  1. Change SelectionUnit to DataGridSelectionUnit.CellOrRowHeader (or Cell) if cell selection is required
  2. Replace cell-selection calls with row selection (set DataGridRow.IsSelected or use SelectedItems)
  3. Wrap cell-selection mutation in a check of SelectionUnit before calling
  4. 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

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


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)