When grip editing a polyline, hovering over a grip shows this context menu:
When the user moves the cursor anywhere but over the menu, the menu is removed and the cursor remains visible at all times. In my GripOverrule, adding a ContextMenu to MyGrip works the same, except the menu stays unless the user clicks elsewhere, and the cursor is not visible unless the user manages to pass over the menu. What must I do to get the same functionality as the polyline grip menu?
Here is a small example which adds a single grip to the endpoint of lines. The OnHover() adds a single menu item to set the line's layer to "0". None of the ContextMenuStrip's properties/events that control the display or focus, other than Show() & Hide(), work.
using System; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Geometry; using Autodesk.AutoCAD.GraphicsInterface; using Autodesk.AutoCAD.Runtime; namespace Test_Commands.GripTest { public class GripTest { private static bool ShouldBeOn = false; [CommandMethod("GripTestToggle")] public void griptestCommand() { if (ShouldBeOn) { ShouldBeOn = false; RemoveOverrule(); } else { ShouldBeOn = true; AddOverrule(); } } // Added for grips for line public static TestGripsOverrule mGripOverrule; public static void AddOverrule() { if (ShouldBeOn) { // Instantiate our global Overrule and set it to overrule lines with my data attached mGripOverrule = new TestGripsOverrule(); Overrule.AddOverrule(RXObject.GetClass(typeof(Line)), mGripOverrule, false); //Turn overruling on Overrule.Overruling = true; } } public static void RemoveOverrule() { if (!ShouldBeOn) { Overrule.RemoveOverrule(RXObject.GetClass(typeof(Line)), mGripOverrule); mGripOverrule = null; } } } //Grip overrule to add our custom grips to the line public class TestGripsOverrule : GripOverrule { public static short gripcolor { get; set; } public static System.Windows.Forms.ContextMenuStrip mnu; //Our custom grip class //(Could have derived one class for each grip, but we'll use member data (Ordinal property) to distinguish grips instead) public class MyGrip : GripData { private int mGripNum; public int Ordinal { get { return mGripNum; } set { mGripNum = value; } } public override bool ViewportDraw(ViewportDraw worldDraw, ObjectId entityId, DrawType type, Point3d? imageGripPoint, int gripSizeInPixels) { Point2d unit = worldDraw.Viewport.GetNumPixelsInUnitSquare(GripPoint); var gripPolygonPts = new Point3dCollection(); double lengthVal = 1.20 * gripSizeInPixels / unit.X; gripPolygonPts.Add(new Point3d(GripPoint.X, GripPoint.Y + lengthVal, 0)); gripPolygonPts.Add(new Point3d(GripPoint.X + lengthVal, GripPoint.Y, 0)); gripPolygonPts.Add(new Point3d(GripPoint.X, GripPoint.Y - lengthVal, 0)); gripPolygonPts.Add(new Point3d(GripPoint.X - lengthVal, GripPoint.Y, 0)); // Polygon properties worldDraw.SubEntityTraits.Color = gripcolor; worldDraw.SubEntityTraits.FillType = FillType.FillAlways; worldDraw.Geometry.Polygon(gripPolygonPts); return true; } private ObjectId entId; public override ReturnValue OnHover(ObjectId entityId, Context contextFlags) { if(mnu==null) { mnu = new System.Windows.Forms.ContextMenuStrip(); mnu.Items.Add("Set Layer 0"); } entId = entityId; Document doc = Application.DocumentManager.MdiActiveDocument; System.Windows.Point pos = doc.Editor.PointToScreen(GripPoint, Convert.ToInt16(Application.GetSystemVariable("CVPORT"))); pos.Offset(doc.Window.DeviceIndependentLocation.X + 15, doc.Window.DeviceIndependentLocation.Y + 38); mnu.Show(new System.Drawing.Point(Convert.ToInt32(pos.X), Convert.ToInt32(pos.Y))); mnu.ItemClicked += mnu_ItemClicked; return base.OnHover(entityId, contextFlags); } void mnu_ItemClicked(object sender, System.Windows.Forms.ToolStripItemClickedEventArgs e) { mnu.Hide(); Document doc = Application.DocumentManager.MdiActiveDocument; using (DocumentLock doclock = doc.LockDocument()) { Line line = (Line)entId.Open(OpenMode.ForWrite); line.Layer = "0"; line.Close(); } mnu.ItemClicked -= mnu_ItemClicked; } } //Array to hold our grip public GripData[] mGripData = new GripData[1]; public override void GetGripPoints(Autodesk.AutoCAD.DatabaseServices.Entity entity, Autodesk.AutoCAD.DatabaseServices.GripDataCollection grips, double curViewUnitSize, int gripSize, Autodesk.AutoCAD.Geometry.Vector3d curViewDir, Autodesk.AutoCAD.DatabaseServices.GetGripPointsFlags bitFlags) { //We assume entity is a line Line line = entity as Line; if (line == null) return; gripcolor = (short)Application.GetSystemVariable("GRIPHOT"); // Set point at end of line MyGrip grip = new MyGrip(); grip.Ordinal = 0; mGripData[0] = grip; UpdateGripLocations(line); //Add our grips to the list foreach (MyGrip g in mGripData) { grips.Add(g); } //Get the standard line grip points as well, but not wanted for this test //base.GetGripPoints(entity, grips, curViewUnitSize, gripSize, curViewDir, bitFlags); } public override void MoveGripPointsAt(Autodesk.AutoCAD.DatabaseServices.Entity entity, GripDataCollection grips, Vector3d offset, MoveGripPointsFlags bitFlags) { //We only take action when we get this call on a database resident entity //Dragging operation makes shallow clone of line, and setting clomeMeForDragging to false is generally a bad idea. //(If you do set clone me for dragging to false, then don't call base class overriden methods). try { if (entity.Id.IsValid) { var line = entity as Line; //Iterate through list of all grips being moved foreach (GripData g in grips) { if (g is MyGrip) { MyGrip grip = g as MyGrip; //Cast to our grip type double newX = g.GripPoint.X + offset.X; double newY = g.GripPoint.Y + offset.Y; switch (grip.Ordinal) { case 0: line.EndPoint = new Point3d(newX, newY, line.EndPoint.Z); break; default: break; } //Tell grip to move itself long the line UpdateGripLocations(line); } } } } catch (System.Exception) { } } private void UpdateGripLocations(Line line) { Point3d pt = new Point3d(line.EndPoint.X, line.EndPoint.Y, 0); mGripData[0].GripPoint = pt; } } }
Solved! Go to Solution.
Solved by LE3. Go to Solution.
Solved by LE3. Go to Solution.
hi Jeff!, look at the sample provided by Balaji Ram here:
http://forums.autodesk.com/t5/objectarx/how-to-handle-ctrl-click-in-move-grip-point/td-p/3725552
you need to implement the MultiModesGripPE class, Balaji sample covers that in .NET.
hth.-
hi Jeff, good! -- got the chance to play a little bit with Balaji's sample, and did some refactoring, on this one, it simple will extend the start point of a line, see if helps:
public class JeffMoverrule { private static readonly Overrule[] Overrules = { new DrawOverrule(), new PropsOverrule(), new XformOverrule(), new ObjectSnapOverrule(), new GripPointOverrule(), new HiliteOverrule(), new ObjOverrule() }; private static readonly List<ObjectId> OverruledObjects = new List<ObjectId>(); private const string RegAppName = "JeffMoverrule"; private static bool _overruleAdded; [CommandMethod("GRIPMENU")] public static void cmd_gripMenu() { var document = AcadApp.DocumentManager.MdiActiveDocument; var editor = document.Editor; const string classType = "line"; var options = new PromptEntityOptions(string.Format("Select {0} to overrule", classType)); options.SetRejectMessage(string.Format("Selected object is not a {0}!", classType)); options.AddAllowedClass(typeof (Line), true); var getLine = editor.GetEntity(options); if (getLine.Status != PromptStatus.OK) return; if (getLine.ObjectId.ObjectClass != RXObject.GetClass(typeof (Line))) { editor.WriteMessage("Selected object is not a line!\n"); return; } if (OverruledObjects.Count == 0) { AcadApp.DocumentManager.DocumentToBeDestroyed += DocumentManager_DocumentToBeDestroyed; } if (!OverruledObjects.Contains(getLine.ObjectId)) { var database = getLine.ObjectId.Database; using (var transaction = database.TransactionManager.StartTransaction()) { var regAppTable = (RegAppTable) transaction.GetObject(database.RegAppTableId, OpenMode.ForRead, false); if (!regAppTable.Has(RegAppName)) { var regAppTableRecord = new RegAppTableRecord {Name = RegAppName}; regAppTable = (RegAppTable) transaction.GetObject(database.RegAppTableId, OpenMode.ForWrite, false); regAppTable.Add(regAppTableRecord); transaction.AddNewlyCreatedDBObject(regAppTableRecord, true); } var line = (Line) transaction.GetObject(getLine.ObjectId, OpenMode.ForRead); if (line.GetXDataForApplication(RegAppName) == null) { line = (Line) transaction.GetObject(getLine.ObjectId, OpenMode.ForWrite); line.XData = new ResultBuffer(new TypedValue((int) DxfCode.ExtendedDataRegAppName, RegAppName), new TypedValue((int) DxfCode.ExtendedDataReal, 0.0)); OverruledObjects.Add(getLine.ObjectId); } transaction.Commit(); } } var ids = OverruledObjects.ToArray(); foreach (var overrule in Overrules) { overrule.SetIdFilter(ids); if (!_overruleAdded) { Overrule.AddOverrule(RXObject.GetClass(typeof (Line)), overrule, false); } } _overruleAdded = true; AcadApp.DocumentManager.MdiActiveDocument.Editor.Regen(); } private static void DocumentManager_DocumentToBeDestroyed(object sender, DocumentCollectionEventArgs e) { for (var i = OverruledObjects.Count - 1; i >= 0; i--) if (OverruledObjects[i].Database == e.Document.Database) OverruledObjects.RemoveAt(i); var ids = OverruledObjects.ToArray(); foreach (var overrule in Overrules) overrule.SetIdFilter(ids); } private static LineSegment3d[] GetPointsLocations(Line line) { var lines = new LineSegment3d[1]; var segment = new LineSegment3d(line.StartPoint, line.EndPoint); var vector = segment.Direction.MultiplyBy(-(segment.Length/3.0)); lines[0] = new LineSegment3d(segment.StartPoint.Subtract(vector), segment.EndPoint.Add(vector)); return lines; } private class DrawOverrule : DrawableOverrule { public override bool WorldDraw(Drawable drawable, WorldDraw worldDraw) { return base.WorldDraw(drawable, worldDraw); } } private class PropsOverrule : PropertiesOverrule { public override void List(Entity entity) { } } private class XformOverrule : TransformOverrule { public override void Explode(Entity entity, DBObjectCollection entitySet) { } } private class LineOverruled : MultiModesGripPE { public override GripMode CurrentMode(Entity entity, GripData gripData) { var grip = gripData as GripPointOverrule.MyGrip; if (grip == null) return null; var index = (int) grip.CurrentModeId - (int) GripMode.ModeIdentifier.CustomStart; return grip.Modes[index]; } public override uint CurrentModeId(Entity entity, GripData gripData) { var grip = gripData as GripPointOverrule.MyGrip; if (grip != null) return (uint) grip.CurrentModeId; return 0; } public override bool GetGripModes(Entity entity, GripData gripData, GripModeCollection modes, ref uint curMode) { if (!(gripData is GripPointOverrule.MyGrip)) return false; return ((GripPointOverrule.MyGrip) gripData).GetGripModes(ref modes, ref curMode); } public override GripType GetGripType(Entity entity, GripData gripData) { return (gripData is GripPointOverrule.MyGrip) ? GripType.Secondary : GripType.Primary; } public override void Reset(Entity entity) { } public override bool SetCurrentMode(Entity entity, GripData gripData, uint curMode) { if (!(gripData is GripPointOverrule.MyGrip)) return false; ((GripPointOverrule.MyGrip) gripData).CurrentModeId = (GripMode.ModeIdentifier) curMode; return true; } } private class GripPointOverrule : GripOverrule { internal GripPointOverrule() { var theOverruled = new LineOverruled(); GetClass(typeof (Line)).AddX(GetClass(typeof (LineOverruled)), theOverruled); } internal abstract class MyGrip : GripData { protected MyGrip() { _gripModeCollection = new GripModeCollection(); } private GripMode.ModeIdentifier _curModeId = GripMode.ModeIdentifier.CustomStart; public virtual GripMode.ModeIdentifier CurrentModeId { get { return _curModeId; } set { _curModeId = value; } } private readonly GripModeCollection _gripModeCollection; private const short Color = 2; public virtual GripModeCollection Modes { get { return _gripModeCollection; } } public abstract void Extend(Entity entity, Vector3d offset); public abstract bool GetGripModes(ref GripModeCollection modes, ref uint curMode); public override bool ViewportDraw(ViewportDraw viewportDraw, ObjectId entityId, DrawType type, Point3d? imageGripPoint, int gripSize) { var unit = viewportDraw.Viewport.GetNumPixelsInUnitSquare(GripPoint); var gripPolygonPts = new Point3dCollection(); var lengthVal = 1.20*gripSize/unit.X; gripPolygonPts.Add(new Point3d(GripPoint.X, GripPoint.Y + lengthVal, 0)); gripPolygonPts.Add(new Point3d(GripPoint.X + lengthVal, GripPoint.Y, 0)); gripPolygonPts.Add(new Point3d(GripPoint.X, GripPoint.Y - lengthVal, 0)); gripPolygonPts.Add(new Point3d(GripPoint.X - lengthVal, GripPoint.Y, 0)); viewportDraw.SubEntityTraits.Color = Color; viewportDraw.SubEntityTraits.FillType = FillType.FillAlways; viewportDraw.Geometry.Polygon(gripPolygonPts); return true; } protected void ExtendEndPoint(Entity entity, Vector3d offset) { var line = (Line) entity; var gripPoint = GripPoint + offset; var length = gripPoint.DistanceTo(line.StartPoint);// + line.Length / 3.0; using (var seg = new LineSegment3d(line.StartPoint, line.EndPoint)) { var vec = seg.Direction.MultiplyBy(length); using (var segment = new LineSegment3d(seg.StartPoint.Subtract(vec), seg.EndPoint.Add(vec))) { line.StartPoint = segment.StartPoint; } } } } private class LengthThirdFractionGrip : MyGrip { public override bool GetGripModes(ref GripModeCollection modes, ref uint curMode) { var gripMode = new GripMode { ModeId = 0, DisplayString = "Extend Start Point Third Of The Line Length...", Action = GripMode.ActionType.Immediate }; modes.Add(gripMode); return true; } public override void Extend(Entity entity, Vector3d offset) { ExtendEndPoint(entity, offset); } } private readonly GripData[] _grips = new GripData[1]; public override void GetGripPoints(Entity entity, GripDataCollection grips, double curViewUnitSize, int gripSize, Vector3d curViewDir, GetGripPointsFlags bitFlags) { base.GetGripPoints(entity, grips, curViewUnitSize, gripSize, curViewDir, bitFlags); var line = (Line) entity; var lines = GetPointsLocations(line); _grips[0] = new LengthThirdFractionGrip {GripPoint = lines[0].EndPoint}; foreach (var grip in _grips) grips.Add(grip); } public override void MoveGripPointsAt(Entity entity, GripDataCollection grips, Vector3d offset, MoveGripPointsFlags bitFlags) { foreach (var grip in grips) { var myGrip = grip as MyGrip; if (myGrip != null) myGrip.Extend(entity, offset); else base.MoveGripPointsAt(entity, grips, offset, bitFlags); } } } private class ObjectSnapOverrule : OsnapOverrule { public override void GetObjectSnapPoints(Entity entity, ObjectSnapModes snapMode, IntPtr gsSelectionMark, Point3d pickPoint, Point3d lastPoint, Matrix3d viewTransform, Point3dCollection snapPoints, IntegerCollection geometryIds) { base.GetObjectSnapPoints(entity, snapMode, gsSelectionMark, pickPoint, lastPoint, viewTransform, snapPoints, geometryIds); if ((snapMode & ObjectSnapModes.ModeEnd) != ObjectSnapModes.ModeEnd) return; var line = (Line) entity; var locations = GetPointsLocations(line); foreach (var location in locations) { //snapPoints.Add(l.StartPoint); snapPoints.Add(location.EndPoint); } } } private class HiliteOverrule : HighlightOverrule { } private class ObjOverrule : ObjectOverrule { public override void Erase(DBObject dbObject, bool erasing) { base.Erase(dbObject, erasing); if (erasing) throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.CannotBeErasedByCaller); } } }
Hi Luis/Jeff,
Great POst ,
I searched everywehre for an example for this.
it works great.
I have one question thought, How can I add one more item to menu.
would you mind to trim your code to show a list box with 2 items please.
Thanks,
Janet.
@JanetDavidson, you basically just need to add GripData for each entry you want to see in the contect menu.
var gripMode = new GripMode { ModeId = 0, DisplayString = "Extend Start Point Third Of The Line Length...", Action = GripMode.ActionType.Immediate }; modes.Add(gripMode); //next one gripMode = new GripMode { ModeId = 1, DisplayString = "Something else to display.", Action = GripMode.ActionType.Immediate }; modes.Add(gripMode); //etc
Hello Jeff,
Thanks for looking in this for me.
I did exactly what you said and now in context menu it shows two items.
But how can I assign a different task for second item ?
Let's say the second item should change the layer of line to something else?
That part is the tricky part.
Funny is, right now after I added the second item, when I click on it , it will extend the line , like I clicked the first item.
I really got confused with this grip overrule.
Cheers,
Janet.
Hello Jeff, thanks for your helpful example. Have you find the way to make the context menu auto hide ? Do you have a final solution for grip menu? I am looking forward to your update.
Regards.
Page.
Page,
using the MultiModesGripPE class was the key to getting the context menu to auto-hide. Attached is a class showing how this works, along with 2 options for the overruled grip...one to extend from the start point of a line, the other to extend from the end point.
//add event handler mnu.MouseLeave += Mnu_MouseLeave; //in the handler private void Mnu_MouseLeave(object sender, EventArgs e) { System.Windows.Forms.ContextMenuStrip mnu = sender as System.Windows.Forms.ContextMenuStrip; mnu.Hide(); }
Hi Jeff,
I am wondering if this routine could be applied to Associative Arrays?
Is it feasible ?
Regards,
Janet.