C# .NET API · Interactive Jigs

Multi-Entity Interactive Placement & Dynamic Dimensioning with DrawJig and EntityJig (dev-cookbook-25)

Master EntityJig and DrawJig in GstarCAD 2027 C# .NET to build interactive rubber-band previews for structural trusses, pipe assemblies, and dynamic angular dimensions.

1. Choosing Between `EntityJig` and `DrawJig` in GstarCAD `.NET`

Interactive graphical placement—where geometry dynamically stretches, rotates, and displays live dimensional feedback at 60 FPS as the engineer moves the crosshair—is powered by the Jig Engine (`Gssoft.GstarCAD.EditorInput`) in GstarCAD 2027. The SDK provides two abstract base classes tailored to different geometric cardinalities:

  • EntityJig (Single Database Entity): Wraps exactly one memory-resident `Entity` instance (such as a single `BlockReference`, `Polyline`, or custom `AcDbEntity`). It supports built-in Dynamic Input (`DIMINPUT`) dimensional heads-up boxes via `DynamicDimensionDataCollection`.
  • DrawJig (Multi-Entity Assemblies & Procedural Previews): Does not bind to a single entity. Instead, its `WorldDraw(WorldDraw draw)` override allows you to render dozens of synchronized primitives—such as a complete steel portal frame (two columns, two rafters, haunch stiffeners, and live span labels)—simultaneously during mouse drag before appending them to Model Space in a single transaction.

2. C# .NET Implementation: Multi-Entity Structural Portal Frame `DrawJig`

The C# `DrawJig` below prompts the engineer for a base point, tracks the cursor via `Prompts.AcquirePoint()`, suppresses micro-jitter below `1e-4` mm, and renders a live multi-member steel frame preview before committing the entities to the database:

using System;
using Gssoft.GstarCAD.Runtime;
using Gssoft.GstarCAD.ApplicationServices;
using Gssoft.GstarCAD.DatabaseServices;
using Gssoft.GstarCAD.EditorInput;
using Gssoft.GstarCAD.Geometry;
using Gssoft.GstarCAD.GraphicsInterface;

namespace GstarCadSamples.JigEngine
{
    public class PortalFrameDrawJig : DrawJig
    {
        private readonly Point3d _basePt;
        private Point3d _cursorPt;
        private readonly double _columnHeight;

        public PortalFrameDrawJig(Point3d basePt, double columnHeight)
        {
            _basePt = basePt;
            _cursorPt = basePt + new Vector3d(6000.0, 0, 0);
            _columnHeight = columnHeight;
        }

        protected override SamplerStatus Sampler(JigPrompts prompts)
        {
            JigPromptPointOptions opts = new JigPromptPointOptions("
Specify right column base point: ")
            {
                BasePoint = _basePt,
                UseBasePoint = true,
                Cursor = CursorType.RubberBand,
                UserInputControls = UserInputControls.Accept3dCoordinates
            };

            PromptPointResult res = prompts.AcquirePoint(opts);
            if (res.Status != PromptStatus.OK) return SamplerStatus.Cancel;

            // Prevent redundant WorldDraw calls when mouse hasn't moved
            if (_cursorPt.DistanceTo(res.Value) < 1e-4)
                return SamplerStatus.NoChange;

            _cursorPt = res.Value;
            return SamplerStatus.OK;
        }

        protected override bool WorldDraw(WorldDraw draw)
        {
            Vector3d spanVec = _cursorPt - _basePt;
            if (spanVec.Length < 500.0) return true;

            Point3d eaveLeft  = _basePt + new Vector3d(0, _columnHeight, 0);
            Point3d eaveRight = new Point3d(_cursorPt.X, _basePt.Y + _columnHeight, 0);
            Point3d apexPt    = new Point3d((_basePt.X + _cursorPt.X) * 0.5, _basePt.Y + _columnHeight + (Math.Abs(spanVec.X) * 0.15), 0);

            WorldGeometry geo = draw.Geometry;
            geo.WorldLine(_basePt, eaveLeft);     // Left Column
            geo.WorldLine(new Point3d(_cursorPt.X, _basePt.Y, 0), eaveRight); // Right Column
            geo.WorldLine(eaveLeft, apexPt);      // Left Rafter
            geo.WorldLine(apexPt, eaveRight);     // Right Rafter
            return true;
        }
    }
}

3. Critical Jig Memory & Disposal Discipline in `.NET`

Inside `DrawJig.WorldDraw()`, if you instantiate temporary in-memory `Entity` objects (such as `new MText()` or `new Hatch()`) and pass them to `draw.Geometry.Draw(tempEnt)`, you **must** wrap those temporary non-database-resident entities inside a `using (...)` block so their underlying C++ `AcDbObject` wrappers are deterministically disposed on every mouse frame. Failing to dispose non-DBRO entities inside a 60 Hz Jig loop leaks unmanaged heap memory and can trigger garbage-collection pauses mid-drag.

4. Adding Interactive Keyword Switching (`[Flip/SectionSize/PinBase]`) Inside `Sampler()`

To let the engineer switch steel profile sizes or flip frame orientation mid-drag without exiting the `DrawJig`, register keywords on your `JigPromptPointOptions` via `opts.Keywords.Add("Flip")` and `opts.Keywords.Add("Profile")` before calling `prompts.AcquirePoint(opts)`.

When the user presses `F` or clicks `Flip` on the command line, `res.Status` returns `PromptStatus.Keyword` and `res.StringResult` contains `"Flip"`. Toggle your internal boolean `_isFlipped = !_isFlipped` and return `SamplerStatus.OK` so `WorldDraw()` immediately redraws the mirrored portal frame on the very next frame.

Validate This Workflow in GstarCAD 2027

Engineered by Suzhou Gstarsoft Co., Ltd. (SSE: 688657), GstarCAD 2027 delivers 100% native AC1032 DWG binary fidelity, perpetual licensing sovereignty, and zero-day AutoCAD command parity.

Download Free 30-Day Evaluation → Explore AutoCAD Migration Hub Take Skill Certification Quiz