Namespace: Beep.Skia.Canvas — Type: class — Partial Files: 5

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 ClassResponsibility
SelectionManagerComponent/line selection, rubber-band marquee, clipboard operations (355 lines).
InteractionHelperMouse event routing, drag modes, hit-test priority chain resolution.
RenderingHelperGrid rendering, pan/zoom transform application, canvas state management.
HistoryManagerCommand-pattern undo/redo stack for all diagram mutations.
ConnectionManagerConnection line validation, routing, and state tracking.
ComponentManagerAdd/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:

FileContentsKey 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();
MethodSignatureDescription
AddComponentvoid AddComponent(IDrawableComponent component)Adds a component to the diagram, assigns it the next available Z-order, triggers ComponentAdded event, and invalidates the canvas.
RemoveComponentvoid 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.
FindComponentAtIDrawableComponent? FindComponentAt(SKPoint worldPoint)Returns the topmost component at the given world-space point. Iterates components in reverse Z-order for correct hit-test priority.
BringToFrontvoid BringToFront(IDrawableComponent c)Sets the component's Z-order to the maximum value, ensuring it renders above all others.
SendToBackvoid 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);
MethodDescription
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:

MemberTypeDescription
SelectedComponentsIReadOnlyList<IDrawableComponent>Currently selected components. Multiple selection via rubber-band or Ctrl+click.
SelectedConnectionConnectionLine?The currently selected connection line, if any. Only one connection can be selected at a time.
SelectAll()voidSelects every component on the canvas.
ClearSelection()voidDeselects all components and connections. Fires SelectionChanged.
DeleteSelected()voidRemoves all selected components and connections, pushing actions onto the undo stack.
CopySelected()voidSerializes selected components to the clipboard as JSON DiagramDto.
PasteClipboard()voidDeserializes 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 / MethodTypeDescription
ZoomFactorfloatCurrent zoom level. Range: 0.1f to 5.0f. 1.0f = 100% (no zoom).
PanOffsetSKPointCurrent scroll offset in world-space units. Negative values pan the viewport right/down.
ResetView()voidResets ZoomFactor to 1.0f and PanOffset to (0,0). Useful for "fit to screen" or "reset view" toolbar buttons.
ZoomToFit()voidCalculates 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)SKPointConverts a screen-space pixel coordinate to world-space by reversing the pan/zoom transform.
WorldToScreen(SKPoint worldPt)SKPointConverts 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:

EventDelegateFired When
ComponentAddedEventHandler<ComponentEventArgs>A component is added to the canvas via AddComponent() or paste.
ComponentRemovedEventHandler<ComponentEventArgs>A component is removed from the canvas via RemoveComponent() or delete.
SelectionChangedEventHandler<SelectionEventArgs>Selection state changes: component selected, deselected, or multi-select rubber-band completed.
ConnectionEstablishedEventHandler<ConnectionEventArgs>A new connection line is created between two valid ports.
ConnectionRemovedEventHandler<ConnectionEventArgs>A connection line is disconnected or its owning component is removed.
ComponentMovedEventHandler<ComponentEventArgs>A component's X/Y position changes (fired on drag completion, not during drag).
ViewportChangedEventHandlerZoomFactor or PanOffset changes, requiring the viewport to repaint.
DiagramDirtyEventHandlerAny 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);
};