.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

GripData.OnHover() how to add a context menu

15 REPLIES 15
SOLVED
Reply
Message 1 of 16
Jeff_M
3250 Views, 15 Replies

GripData.OnHover() how to add a context menu

When grip editing a polyline, hovering over a grip shows this context menu:

poly grip menu.png

 

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;
        }

    }
}

 

 

Jeff_M, also a frequent Swamper
EESignature
15 REPLIES 15
Message 2 of 16
LE3
Advocate
in reply to: Jeff_M

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.-

Message 3 of 16
Jeff_M
in reply to: LE3

Thanks, Luis! I've been searching for an example like this for a few days. I think this also will help to solve an issue I posted about on the Swamp yesterday.
Jeff_M, also a frequent Swamper
EESignature
Message 4 of 16
LE3
Advocate
in reply to: Jeff_M

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);
            }
        }
    }
Message 5 of 16
Jeff_M
in reply to: LE3

Thanks again, Luis! It took a mix of the DevDay and your examples to get this, but it's now working just as I had expected it to. I never wuld've figured it out without your help, much appreciated!

 

 

Jeff_M, also a frequent Swamper
EESignature
Message 6 of 16
LE3
Advocate
in reply to: Jeff_M

those are great news Jeff, glad i helped.

take care! - luis.-

Message 7 of 16
JanetDavidson
in reply to: LE3

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.

 

Message 8 of 16
Jeff_M
in reply to: JanetDavidson

@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
Jeff_M, also a frequent Swamper
EESignature
Message 9 of 16
JanetDavidson
in reply to: Jeff_M

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.

 

Message 10 of 16
Jeff_M
in reply to: JanetDavidson

Yes, it took me some time to get mine figured out and working as I wanted. I think I need to create a small example, for my own benefit as well as something I can share. My benefit because it's been 10 months since I was working on that and am a bit confused myself as I look through the code. 🙂 Not sure how soon I can get to that though.
Jeff_M, also a frequent Swamper
EESignature
Message 11 of 16
sdphg
in reply to: Jeff_M

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.

Message 12 of 16
Jeff_M
in reply to: sdphg

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.

Jeff_M, also a frequent Swamper
EESignature
Message 13 of 16
JanetDavidson
in reply to: Jeff_M

Jeff,

You are the hero.

Thanks a lot .

believe it or not I was struggling with this all these days.

works like a charm.

Thank you again.

Spoiler
Spoiler
Janet.

Message 14 of 16
sdphg
in reply to: Jeff_M

Great work!
Thank you Jeff, this will help lots of people.
BTW, I found a way to auto-hide the context menu. After try some context menu events, I found the MouseLeave can do the hide job, I add some code like below:
//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();
}
 
when the mouse leave the context menu, the menu auto-hide, so far so good.
 
 
 
I chose your context menu solution, becasue I have some additional requirements, and I don't know if the MultiModesGripPE can meets. Here are my requirements:
 
1. I need sub menu, not only one level menu.
 
2. I need more than one grip point, and each of them show different context menu.
 
I don't have much time to go deep in MultiModesGripPE scenario, so I think the context menu is more flexible and much easier to understand.
 
I don't know if the context menu scenario has disadvantages compare to MultiModesGripPE, as I said, so far so good.Smiley Happy
 
Regards.
Page.
Message 15 of 16
Jeff_M
in reply to: sdphg

1. No, no submenus that I can find.
2. I believe this would be possible, but if you need 1., too, then it's a moot point.
Jeff_M, also a frequent Swamper
EESignature
Message 16 of 16
JanetDavidson
in reply to: Jeff_M

Hi Jeff,

I am wondering if this routine could be applied to Associative Arrays?

Is it feasible ?

Regards,

Janet.

 

 

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost