Skip to content

FindHighlightAdorner

317jamtay317 edited this page Jul 23, 2026 · 2 revisions

FindHighlightAdorner

The FindHighlightAdorner draws the Find highlights over the MarkdownRichEditor's Visual Document: a translucent fill behind every Match, with the Current Match filled more strongly and outlined. It is the visible half of Find; the matching itself is the pure MatchFinder.

  • Class: UI.Controls.FindHighlightAdorner (derives from System.Windows.Documents.Adorner)
  • Attached by: MarkdownRichEditor on Loaded, into the editor's AdornerLayer.

Division of labour

Find is split so that only the editor Control touches the document:

  1. The editor computes. MarkdownRichEditor builds a plain-text snapshot of the Visual Document (concatenating text runs, with a separator inserted at block boundaries so a Match never bridges two blocks), runs MatchFinder over it, and maps each Match back to a TextRange. It tracks which Match is the Current Match and exposes MatchCount / MatchSummary for the Find Bar.
  2. The adorner draws. The editor calls Update with the Match ranges and the Current Match index; OnRender paints a rounded fill behind each on-screen Match, using a stronger fill and an outline for the Current Match. SetColors lets the editor pass palette-derived brushes so the highlights follow the light/dark theme.

How it works

  • Repaint (cheap). Scrolling and resizing only repaint from the ranges already held — they never re-run the search. Off-screen and wrapped Matches are skipped rather than painted.
  • View-only (INV-016). The adorner is presentation-only (IsHitTestVisible = false) and only ever draws. Neither it nor the Find state on the editor feeds back into Capture, so finding, highlighting, and moving the Current Match never change the Markdown Document. Replacing a Match does edit (INV-022), but that is the editor's own Replace / Replace All — the adorner is not involved, and simply repaints from the fresh ranges afterwards.
  • Stale pointers. When the document is replaced the editor recomputes the ranges; a repaint that races that replacement swallows the resulting InvalidOperationException and waits for the fresh ranges.

Clone this wiki locally