dotnet/wpf · error · ArgumentException
Unrecognized Stroke in Stroke.Invalidated event arguments.
Error message
Unrecognized Stroke in Stroke.Invalidated event arguments.
What it means
Renderer.OnStrokeInvalidated throws ArgumentException (SR.UnknownStroke1) when a Stroke.Invalidated event fires from a stroke that has no entry in the Renderer's _visuals map. The renderer caches a StrokeVisual per stroke; invalidated strokes must be ones it renders.
Solutions
- Stop holding/mutating Stroke references after removing them from the rendered collection
- Attach strokes to only one Renderer at a time
- Re-add the stroke to the StrokeCollection before mutating it if edits are intended
- Guard mutations with a check that the stroke belongs to the live collection
Example fix
// before
stroke.StylusPoints = newPoints; // stroke already removed from collection
// after
if (strokeCollection.Contains(stroke)) { stroke.StylusPoints = newPoints; } Defensive patterns
Strategy: validation
Validate before calling
// Only mutate strokes that are still in the rendered collection if (strokeCollection.Contains(stroke)) stroke.StylusPoints = newPoints;
Type guard
bool IsTrackedStroke(StrokeCollection c, Stroke s) => c?.Contains(s) == true;
Try / catch
try { stroke.StylusPoints = newPoints; }
catch (ArgumentException) { /* stroke no longer tracked - drop edit */ } Prevention
- Drop references to strokes once removed from the collection
- Attach each stroke to at most one renderer at a time
- Marshal stroke mutations to the UI thread
- Avoid sharing Stroke instances across InkCanvas instances
When it happens
Trigger: Modifying a Stroke (points, drawing attributes) that is not attached to this Renderer — e.g. a stroke removed from the collection but still referenced, or a stroke shared across renderers where one renderer never tracked it.
Common situations: Retaining Stroke references after removing them from a StrokeCollection and mutating them later, sharing strokes between multiple InkCanvases/renderers, background thread mutations after detach.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Duplicate Stroke in StrokeCollectionChangedEventArgs.Added.
- Unrecognized Stroke in…
- SR.EventArgIsNull
- SR.EventArgIsNull
- ' '.' ' is a property without a getter and is not a valid…
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/61be4086be37f0b9.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Ink/Renderer.cs:481
{
throw new System.ArgumentException(SR.UnknownStroke3);
}
}
}
/// <summary>
/// Stroke Invalidated event handler
/// </summary>
private void OnStrokeInvalidated(object sender, EventArgs eventArgs)
{
System.Diagnostics.Debug.Assert(_strokes.IndexOf(sender as Stroke) != -1);
// Find the visual associated with the changed stroke.
StrokeVisual visual;
Stroke stroke = (Stroke)sender;
if (!_visuals.TryGetValue(stroke, out visual))
{
throw new System.ArgumentException(SR.UnknownStroke1);
}
// The original value of IsHighligher and Color are cached in StrokeVisual.
// if (IsHighlighter value changed or (IsHighlighter == true and not changed and color changed)
// detach and re-attach the corresponding visual;
// otherwise Invalidate the corresponding StrokeVisual
if (visual.CachedIsHighlighter != stroke.DrawingAttributes.IsHighlighter ||
(stroke.DrawingAttributes.IsHighlighter &&
StrokeRenderer.GetHighlighterColor(visual.CachedColor) != StrokeRenderer.GetHighlighterColor(stroke.DrawingAttributes.Color)))
{
// The change requires reparenting the visual in the tree.
DetachVisual(visual);
AttachVisual(visual, buildingStrokeCollection: false);
// Update the cached values
visual.CachedIsHighlighter = stroke.DrawingAttributes.IsHighlighter;
visual.CachedColor = stroke.DrawingAttributes.Color;
}View on GitHub (pinned to 81131a70a4)