DrawingManager
The central coordinator — split across 5 partial files
Namespace: Beep.Skia.Canvas — Type: class — Partial Files: 5
Table of Contents
Overview
DrawingManager is the central coordinator of the Beep.Skia canvas ecosystem. It maintains the master list of all components and connection lines, routes mouse and keyboard interactions to the appropriate handlers, manages zooming and panning transforms, renders the background grid, and provides the public API for adding, removing, connecting, and disconnecting diagram elements.
Rather than being a monolithic class, DrawingManager delegates specialized responsibilities to a set of helper classes:
| Helper Class | Responsibility |
|---|---|
SelectionManager | Component/line selection, rubber-band marquee, clipboard operations (355 lines). |
InteractionHelper | Mouse event routing, drag modes, hit-test priority chain resolution. |
RenderingHelper | Grid rendering, pan/zoom transform application, canvas state management. |
HistoryManager | Command-pattern undo/redo stack for all diagram mutations. |
ConnectionManager | Connection line validation, routing, and state tracking. |
ComponentManager | Add/remove/find operations with Z-order and lookup index maintenance. |
Creating a DrawingManager Instance
using SkiaSharp;
var drawingManager = new DrawingManager();
using var surface = SKSurface.Create(new SKImageInfo(1200, 800));
var canvas = surface.Canvas;
drawingManager.GridSpacing = 20;
drawingManager.ShowGrid = true;
drawingManager.SnapToGrid = true;
drawingManager.RequestRedraw();
Partial Files
The DrawingManager class is split across 5 partial files for maintainability and clear separation of concerns:
| File | Contents | Key Members |
|---|---|---|
DrawingManager.Core.cs |
Properties, events, initialization, canvas attachment, dispose pattern. | Canvas, ShowGrid, GridSpacing, SnapToGrid, WorldOverlay, SelectionManager, InteractionHelper, HistoryManager |
DrawingManager.Components.cs |
Component addition, removal, lookup, Z-order management, re-parenting. | AddComponent(), RemoveComponent(), FindComponentAt(), BringToFront(), SendToBack() |
DrawingManager.Connections.cs |
Connection creation, validation, disconnection, line routing, port binding. | Connect(), Disconnect(), GetConnectionsFor(), ValidateConnection() |
DrawingManager.Interaction.cs |
Mouse and keyboard event routing, drag modes, hit testing, cursor management. | OnMouseDown(), OnMouseMove(), OnMouseUp(), OnKeyDown(), CurrentDragMode |
DrawingManager.Rendering.cs |
Canvas drawing orchestration, grid rendering, component iteration for paint. | Render(), DrawGrid(), DrawComponents(), DrawConnections(), DrawSelection() |
Component Management
Components are the primary entities managed by DrawingManager. The component API handles the full lifecycle of diagram nodes:
Adding and Removing Components
var node = new ProcessNode("Validate Order");
node.X = 300;
node.Y = 150;
drawingManager.AddComponent(node);
var found = drawingManager.FindComponentAt(new SKPoint(310, 160));
if (found != null)
{
Console.WriteLine($"Found component: {((SkiaComponent)found).NodeProperties["Text"].Value}");
}
drawingManager.RemoveComponent(node);
node.Dispose();
| Method | Signature | Description |
|---|---|---|
AddComponent | void AddComponent(IDrawableComponent component) | Adds a component to the diagram, assigns it the next available Z-order, triggers ComponentAdded event, and invalidates the canvas. |
RemoveComponent | void RemoveComponent(IDrawableComponent component) | Removes the component, disconnects all attached connection lines, triggers ComponentRemoved event, and invalidates the canvas. Does not call Dispose() — caller is responsible. |
FindComponentAt | IDrawableComponent? FindComponentAt(SKPoint worldPoint) | Returns the topmost component at the given world-space point. Iterates components in reverse Z-order for correct hit-test priority. |
BringToFront | void BringToFront(IDrawableComponent c) | Sets the component's Z-order to the maximum value, ensuring it renders above all others. |
SendToBack | void SendToBack(IDrawableComponent c) | Sets the component's Z-order to the minimum value, rendering it below all others. |
Connection Management
Connection lines visually link two ports and represent relationships between diagram components. The DrawingManager provides a type-safe API for creating, validating, and removing connections:
Connecting and Disconnecting Components
drawingManager.ConnectComponents(startNode, endNode);
// Style the newest line
var connection = drawingManager.GetLines().Last();
connection.Paint.PathEffect = SKPathEffect.CreateDash(new[] { 6f, 4f }, 0);
connection.RoutingMode = LineRoutingMode.Orthogonal;
drawingManager.DisconnectComponents(startNode, endNode);
| Method | Description |
|---|---|
Connect(IConnectionPoint start, IConnectionPoint end) | Validates type compatibility, creates a ConnectionLine, binds it to both ports, adds to the connection list, and fires ConnectionEstablished. Returns null if validation fails. |
Disconnect(ConnectionLine line) | Unbinds the line from both ports, removes from the connection list, and invalidates the canvas. |
GetConnectionsFor(IDrawableComponent component) | Returns all ConnectionLine instances where the given component is either the start or end point. |
ValidateConnection(IConnectionPoint start, IConnectionPoint end) | Checks type compatibility (DataType matching), port availability (not already connected if single-connection), and prevents self-loops unless explicitly allowed. |
Selection
Selection state is managed cooperatively between DrawingManager and SelectionManager. The drawing manager exposes the selection API while delegating implementation to the selection helper:
| Member | Type | Description |
|---|---|---|
SelectedComponents | IReadOnlyList<IDrawableComponent> | Currently selected components. Multiple selection via rubber-band or Ctrl+click. |
SelectedConnection | ConnectionLine? | The currently selected connection line, if any. Only one connection can be selected at a time. |
SelectAll() | void | Selects every component on the canvas. |
ClearSelection() | void | Deselects all components and connections. Fires SelectionChanged. |
DeleteSelected() | void | Removes all selected components and connections, pushing actions onto the undo stack. |
CopySelected() | void | Serializes selected components to the clipboard as JSON DiagramDto. |
PasteClipboard() | void | Deserializes clipboard JSON, instantiates new components with offset positions, adds to canvas. |
Zoom & Pan
The DrawingManager exposes viewport controls that transform the canvas without modifying individual component positions:
| Property / Method | Type | Description |
|---|---|---|
ZoomFactor | float | Current zoom level. Range: 0.1f to 5.0f. 1.0f = 100% (no zoom). |
PanOffset | SKPoint | Current scroll offset in world-space units. Negative values pan the viewport right/down. |
ResetView() | void | Resets ZoomFactor to 1.0f and PanOffset to (0,0). Useful for "fit to screen" or "reset view" toolbar buttons. |
ZoomToFit() | void | Calculates the bounding box of all components and adjusts ZoomFactor and PanOffset so the entire diagram fits within the visible canvas area with padding. |
ScreenToWorld(SKPoint screenPt) | SKPoint | Converts a screen-space pixel coordinate to world-space by reversing the pan/zoom transform. |
WorldToScreen(SKPoint worldPt) | SKPoint | Converts a world-space coordinate to screen-space by applying the pan/zoom transform. |
Programmatic Zoom and Pan
drawingManager.ZoomFactor = 1.5f;
drawingManager.PanOffset = new SKPoint(-100, -50);
var worldPoint = drawingManager.ScreenToWorld(
new SKPoint(mouseX, mouseY));
drawingManager.ResetView();
drawingManager.ZoomToFit();
Events
DrawingManager exposes a rich set of events that allow external code to react to diagram state changes:
| Event | Delegate | Fired When |
|---|---|---|
ComponentAdded | EventHandler<ComponentEventArgs> | A component is added to the canvas via AddComponent() or paste. |
ComponentRemoved | EventHandler<ComponentEventArgs> | A component is removed from the canvas via RemoveComponent() or delete. |
SelectionChanged | EventHandler<SelectionEventArgs> | Selection state changes: component selected, deselected, or multi-select rubber-band completed. |
ConnectionEstablished | EventHandler<ConnectionEventArgs> | A new connection line is created between two valid ports. |
ConnectionRemoved | EventHandler<ConnectionEventArgs> | A connection line is disconnected or its owning component is removed. |
ComponentMoved | EventHandler<ComponentEventArgs> | A component's X/Y position changes (fired on drag completion, not during drag). |
ViewportChanged | EventHandler | ZoomFactor or PanOffset changes, requiring the viewport to repaint. |
DiagramDirty | EventHandler | Any state change that requires a canvas repaint. Components subscribe internally. |
Subscribing to DrawingManager Events
drawingManager.ComponentAdded += (sender, args) =>
{
Console.WriteLine($"Component added: {args.Component.GetType().Name}");
UpdatePropertyGrid(args.Component);
};
drawingManager.ConnectionEstablished += (sender, args) =>
{
Console.WriteLine($"Connected: {args.Connection.Start.Component} -> " +
$"{args.Connection.EndPoint.Owner}");
ValidateDiagramIntegrity();
};
drawingManager.SelectionChanged += (sender, args) =>
{
UpdateToolbarState(
drawingManager.SelectedComponents.Any(),
drawingManager.SelectedConnection != null);
};