-
Notifications
You must be signed in to change notification settings - Fork 143
Rendering Engine
The HexEditor uses a DrawingContext-based rendering pipeline that draws directly onto a Visual — no intermediate UI elements, no layout passes per byte.
| Approach | 50 000 bytes | Notes |
|---|---|---|
Legacy (V1) — ItemsControl + TextBlock per byte |
~3 000 ms | WPF layout for every element |
Modern (V2+) — DrawingContext on DrawingVisual
|
~30 ms | Single pass, one visual |
Result: 99% faster. The rendering path is the critical hot loop — it runs on every scroll tick, resize, and selection change.
flowchart TD
VM["HexEditorViewModel\nScrollPosition · Selection · BytePerLine"]
BP["ByteProvider\nvirtual byte stream"]
VP["HexViewport\nDrawingVisual host"]
DC["DrawingContext\ndc.DrawText() · dc.DrawRectangle()"]
HL["HighlightService\ncolor overlays"]
CBG["CustomBackgroundService\nuser-defined color blocks"]
Sel["SelectionService\nselection rectangle"]
VM --> VP
BP --> VP
VP --> DC
HL --> DC
CBG --> DC
Sel --> DC
HexViewport extends FrameworkElement and overrides OnRender. Only the visible region is drawn on each frame:
Visible lines = Math.Floor(ViewportHeight / LineHeight)
First byte = ScrollPosition × BytePerLine
Last byte = FirstByte + (VisibleLines × BytePerLine)
On scroll, only the changed delta is redrawn (partial invalidation).
Each line draws three columns left to right:
[Offset col] [Hex col] [ASCII col]
00000000 41 42 43 20 48 65 6C 6C 6F 00 ABC Hello.
Each column is a single DrawingContext pass with dc.DrawText() calls using pre-formatted FormattedText objects.
protected override void OnRender(DrawingContext dc)
{
// Background
dc.DrawRectangle(BackgroundBrush, null, new Rect(RenderSize));
for (int line = firstLine; line < lastLine; line++)
{
double y = (line - firstLine) * LineHeight;
// Selection highlight
if (lineIsSelected)
dc.DrawRectangle(SelectionBrush, null, new Rect(hexColX, y, hexColWidth, LineHeight));
// Custom background blocks
foreach (var block in customBackgrounds.GetForLine(line))
dc.DrawRectangle(block.Brush, null, BlockRect(block, y));
// Hex bytes
dc.DrawText(formattedHexLine, new Point(hexColX, y));
// ASCII
dc.DrawText(formattedAsciiLine, new Point(asciiColX, y));
// Offset
dc.DrawText(formattedOffset, new Point(offsetColX, y));
}
// Caret
dc.DrawRectangle(CaretBrush, null, CaretRect);
}FormattedText objects are reused across frames for unchanged lines:
Cache key = (lineNumber, firstByteOffset, selectionState, highlightState)
When a byte changes (edit, undo, selection move), only the affected lines are invalidated and their FormattedText rebuilt.
When BytePerGroup = 2 (16-bit display) or 4 (32-bit):
- Bytes are grouped and the group rendered as a single
FormattedText -
ByteOrderServicehandles Hi-Lo / Lo-Hi byte order within the group
flowchart LR
User["User scrolls\nScrollBar"]
SB["VerticalScroll.Value"]
VM["HexEditorViewModel\n.ScrollPosition"]
VP["HexViewport\nInvalidateVisual()"]
User --> SB
SB -->|Scroll event| VM
VM -->|PropertyChanged| VP
Keyboard navigation bypasses the scrollbar event. In that case, VerticalScroll.Value is also set programmatically to keep the scrollbar thumb in sync (the Scroll event fires only on user interaction).
The ASCII column uses EncodingService to convert bytes to display characters:
- Default:
Encoding.ASCII— non-printable →. - TBL mode:
TblServicemaps bytes to ROM character table glyphs - Unicode mode:
Encoding.UTF8with multi-byte grouping
The encoding is applied per-byte during the FormattedText build phase.
| Scenario | Recommendation |
|---|---|
| Very large files (> 1 GB) | Default — ByteProvider reads lazily, viewport only renders visible lines |
| Many custom background blocks |
CustomBackgroundService uses interval tree — O(log n) lookup per line |
| High-DPI displays |
HexViewport reads VisualTreeHelper.GetDpi() and adjusts pixel snapping |
| Slow themes | Brushes are resolved once at startup and cached — no FindResource per frame |
- ByteProvider — data source for the rendering loop
- Architecture — rendering in the full stack
-
API Reference —
BytePerLine,FontSize,HighlightServiceAPI
✨ Wpf HexEditor user control, by Derek Tremblay (derektremblay666@gmail.com) coded for your fun! 😊🤟
- API Reference
- Performance
- Services
- Core Components
- ByteProvider
- Rendering Engine
- Search Architecture
- whfmt.FileFormatCatalog v1.0.0 NuGet (cross-platform net8.0)
- 690+ .whfmt definitions (schema v2.3)
- Structure Editor — block DataGrid, drag-drop, validation, SmartComplete
- WhfmtBrowser/Catalog panels — browse all embedded formats
- AI Assistant (5 providers, 25 MCP tools)
- Tab Groups, Document Structure, Lazy Plugin Loading
- Window Menu + Win32 Fullscreen (
F11) - Git Integration UI (changes, history, blame)
- Shared Undo Engine (HexEditor ↔ CodeEditor)
- Bracket pair colorization, sticky scroll, peek definition
- Format detection hardening (thread-safe, crash guard)