Your first tool

Drawing tools

Your first tool

A complete two-anchor trend line, end to end — metadata, render, hit-test — showing how little code a real drawing tool needs once the base class and render context do their part.

SIMPLE A trend line

The simplest useful tool has two anchors and draws one segment between them. The shape of every drawing tool is the same three steps: declare the [DrawingToolMetadata] attribute, pass the anchor count to the base constructor, then fill in OnRender and HitTest.

In OnRender you convert both anchors to pixels with AnchorToPixel, draw the line with the tool's Color and LineWidthPx, and — only when selected — paint a handle at each end. In HitTest you convert the same anchors and ask DrawingGeometry whether the cursor is within the tolerance of the segment. That is the whole tool.

TrendLine.csusing TradeStrike.Pipeline.Drawing;

[DrawingToolMetadata("Trend Line",
    Category = "Lines", Hotkey = "Alt+T", IconKey = "TrendLine",
    AnchorCount = 2, Order = 0, Description = "Straight line between two points.")]
public sealed class TrendLine : DrawingToolBase
{
    public TrendLine() : base(anchorCount: 2) { }

    public override void OnRender(IDrawingToolRenderContext ctx)
    {
        var (x1, y1) = ctx.AnchorToPixel(Anchors[0]);
        var (x2, y2) = ctx.AnchorToPixel(Anchors[1]);
        ctx.Renderer.DrawLine(x1, y1, x2, y2, Color, LineWidthPx);

        if (ctx.IsSelected)
        {
            ctx.DrawHandle(x1, y1);
            ctx.DrawHandle(x2, y2);
        }
    }

    public override bool HitTest(double px, double py, IDrawingToolRenderContext ctx, double tol = 6)
    {
        var (x1, y1) = ctx.AnchorToPixel(Anchors[0]);
        var (x2, y2) = ctx.AnchorToPixel(Anchors[1]);
        return DrawingGeometry.DistanceToSegment(px, py, x1, y1, x2, y2) <= tol;
    }
}

The metadata attribute

[DrawingToolMetadata] is what makes the tool discoverable. The catalog reads it at assembly-scan time to populate the toolbar menu, hotkeys, icons and anchor counts — no registration boilerplate. Exactly one attribute per tool class.

Field Meaning Default
Name (ctor arg, required) Display name in the toolbar and context menus. Also becomes ToolName.
Category Toolbar submenu group ("Lines", "Shapes", "Fibonacci", …). "Lines"
Hotkey Optional global hotkey string (e.g. "Alt+T") the toolbar keymap arms. null
IconKey Key into the toolbar's icon-resource dictionary. null
AnchorCount Number of clicks the user makes to finalize (the minimum for variable tools). 2
Order Display order within the category; equal values fall back to alphabetical. 0
Description Tooltip text in the toolbar. null
AnchorCount appears twice — keep them consistent. The metadata's AnchorCount drives the placement gesture; the base constructor's anchorCount pre-allocates the storage. For a fixed-anchor tool they must match (both 2 here). The catalog requires a public parameterless constructor, which the trend line satisfies.
Reading anchors. Because the constructor pre-allocates two slots, both Anchors[0] and Anchors[1] are safe to read in OnRender from the very first frame (they are zero-valued until the user clicks, and the host doesn't render a tool until enough anchors are placed). Write anchors only through SetAnchor — never mutate Anchors directly, since it is exposed as read-only.