dotnet/wpf · error · InvalidOperationException

SR.CannotProcessInkCommand

Error message

SR.CannotProcessInkCommand

What it means

StickyNoteControl throws InvalidOperationException when the InkCommand (a RoutedCommand that sets the ink editing mode) is executed on a sticky note whose content is null or is not an Ink sticky note (StickyNoteType.Text). The command only makes sense on notes that hold InkCanvas content. The library guards so the InkCanvasEditingMode is never applied to a non-ink note.

Solutions

  1. Ensure the StickyNoteControl's Content is an InkCanvas (or set its Type to Ink) before invoking InkCommand.
  2. Gate the command with a CanExecute that checks content != null && content.Type == StickyNoteType.Ink.
  3. Use different commands (text editing commands) for text-type sticky notes.

Example fix

// before
snc.SetValue(StickyNoteControl.InkEditingModeProperty, InkCanvasEditingMode.Ink);
// after
if (snc.Content is StickyNoteContentControl c && c.Type == StickyNoteType.Ink)
{
    snc.SetValue(StickyNoteControl.InkEditingModeProperty, InkCanvasEditingMode.Ink);
}
Defensive patterns

Strategy: validation

Validate before calling

bool canInk = snc.Content != null && snc.Content.Type == StickyNoteType.Ink;
if (canInk) StickyNoteControl.InkCommand.Execute(mode, snc);

Type guard

static bool IsInkNote(StickyNoteControl s) => s?.Content is StickyNoteContentControl c && c.Type == StickyNoteType.Ink;

Try / catch

try { StickyNoteControl.InkCommand.Execute(mode, snc); }
catch (InvalidOperationException ex) { Log.Warn("InkCommand ignored: note is not ink-capable", ex); }

Prevention

When it happens

Trigger: Executing StickyNoteControl.InkCommand (e.g. via a CommandBinding, toolbar button, or InputBinding) while the targeted StickyNoteControl has no content (Content == null) or its Content.Type is StickyNoteType.Text instead of StickyNoteType.Ink.

Common situations: XAML toolbars that wire InkCommand globally so it fires for every sticky note including text notes; notes created without content yet; notes whose content was programmatically switched to text while the command was still bound.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/0189be31f684fc56. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/StickyNote.cs:1679

            RoutedCommand command = (RoutedCommand)(args.Command);
            StickyNoteControl snc = sender as StickyNoteControl;

            Invariant.Assert(snc != null, "Unexpected Commands");
            Invariant.Assert(command == StickyNoteControl.DeleteNoteCommand
                || command == StickyNoteControl.InkCommand, "Unknown Commands");

            if ( command == StickyNoteControl.DeleteNoteCommand )
            {
                // DeleteNote Command
                snc.DeleteStickyNote();
            }
            else if (command == StickyNoteControl.InkCommand)
            {
                StickyNoteContentControl content = snc.Content;

                if (content == null || content.Type != StickyNoteType.Ink)
                {
                    throw new InvalidOperationException(SR.CannotProcessInkCommand);
                }

                // Set the StickyNoteControl's ink editing mode to the command's parameter
                InkCanvasEditingMode mode = (InkCanvasEditingMode)args.Parameter;
                snc.SetValue(InkEditingModeProperty, mode);
            }
        }

        /// <summary>
        /// QueryCommandEnabled Handler - determines if a command should be enabled or not.
        ///   DeleteNoteCommand - if the SNC has an attached annotation
        ///   InkCommand - if the SNC is of type InkStickyNote
        ///   AllOthers - if focus is on our inner control, we let the inner control decide,
        ///               otherwise we return false
        /// </summary>
        private static void _OnQueryCommandEnabled(object sender, CanExecuteRoutedEventArgs args)
        {
            RoutedCommand command = (RoutedCommand)( args.Command );

View on GitHub (pinned to 81131a70a4)