Your first tool
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.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.