dotnet/wpf · error · InvalidOperationException

SR.Calendar_CheckSelectionMode_InvalidOperation

Error message

SR.Calendar_CheckSelectionMode_InvalidOperation

What it means

SelectedDatesCollection.CheckSelectionMode validates that the Calendar's SelectionMode permits programmatic modification of the SelectedDates collection. When SelectionMode is SingleDate and the collection already contains dates, any Add/insert into SelectedDates throws this InvalidOperationException because a SingleDate calendar can only hold one selected date, which must be set via the SelectedDate property instead.

Solutions

  1. Set Calendar.SelectionMode to MultipleRange or SingleRange before adding multiple dates to SelectedDates.
  2. For a single-date selection, set the SelectedDate property instead of calling SelectedDates.Add.
  3. Clear SelectedDates first, or guard the Add with a check of SelectionMode and Count.
  4. Call AddRange only when SelectionMode allows ranges (SingleRange/MultipleRange).

Example fix

// before
calendar.SelectedDates.Add(new DateTime(2026, 5, 1));
// after
if (calendar.SelectionMode == CalendarSelectionMode.SingleDate)
    calendar.SelectedDate = new DateTime(2026, 5, 1);
else
    calendar.SelectedDates.Add(new DateTime(2026, 5, 1));
Defensive patterns

Strategy: validation

Validate before calling

if (calendar.SelectionMode == CalendarSelectionMode.SingleDate && calendar.SelectedDates.Count > 0)
    throw new InvalidOperationException("Use SelectedDate or change SelectionMode before adding dates.");

Type guard

bool canAddMultiple = calendar.SelectionMode == CalendarSelectionMode.MultipleRange || calendar.SelectionMode == CalendarSelectionMode.SingleRange;

Prevention

When it happens

Trigger: Calling SelectedDates.Add(date) (or InsertItem via collection initializer / AddRange) while Calendar.SelectionMode == CalendarSelectionMode.SingleDate and SelectedDates.Count > 0; e.g. adding a second date to the collection without changing SelectionMode.

Common situations: Developers defaulting SelectionMode to SingleDate (the default) then trying to pre-select multiple dates or add dates programmatically in code; porting code written for MultipleRange/SingleRange calendars to a SingleDate calendar.

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/c67ec32a174d3642. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/SelectedDatesCollection.cs:451

            Debug.Assert(_isAddingRange);

            _isAddingRange = false;
            RaiseSelectionChanged(this._removedItems, this._addedItems);
            this._removedItems.Clear();
            this._addedItems.Clear();
            this._owner.UpdateCellItems();
        }

        private bool CheckSelectionMode()
        {
            if (this._owner.SelectionMode == CalendarSelectionMode.None)
            {
                throw new InvalidOperationException(SR.Calendar_OnSelectedDateChanged_InvalidOperation);
            }

            if (this._owner.SelectionMode == CalendarSelectionMode.SingleDate && this.Count > 0)
            {
                throw new InvalidOperationException(SR.Calendar_CheckSelectionMode_InvalidOperation);
            }

            // if user tries to add an item into the SelectedDates in SingleRange mode, we throw away the old range and replace it with the new one
            // in order to provide the removed items without an additional event, we are calling ClearInternal
            if (this._owner.SelectionMode == CalendarSelectionMode.SingleRange && !_isAddingRange && this.Count > 0)
            {
                this.ClearInternal();
                return true;
            }
            else
            {
                return false;
            }
        }

        private bool IsValidThread()
        {
            return Thread.CurrentThread == this._dispatcherThread;

View on GitHub (pinned to 81131a70a4)