5

Is it possible to change the mouse cursor of an ArcMap addin-tool using Arcobjects? I have the cursor set in the Config.esriaddinx file, however I would like to change the cursor to reflect the underlying functionality of the tool. The tool has select and measure functionality. I have added a second cursor (.cur) file to my project resources in VS 2013. I have found varying solutions, of which none have worked for me, including:

How can I access standard ArcMap cursors?

ArcGIS Add-In Custom Mouse Cursor

Set Cursor Property for ArcGIS 10 Add-in Tool?

HELP: Problems setting mouse cursor for AddIn tool

I have tried changing the build action to AddInContent, Embedded Resource, and Resource. While using Embedded Resource, the following returns "Image format is not valid. The image file may be corrupted. Parameter name: stream"

m_customCursor = New System.Windows.Forms.Cursor(Me.GetType.Assembly.GetManifestResourceStream("MyAddin.customCursorFile.cur"))
MyBase.Cursor = m_customCursor

Using IMouseCursor.SetCursor to change to a builtin cursor seems to work, but the cursor only changes for a moment, and then returns to the cursor set in the Config file.

Dim newCursor As IMouseCursor = New MouseCursorClass
newCursor.SetCursor(2)

I have tried several of the linked esri examples for custom cursors without any success.

Is there a definitive method that can be used to dynamically change the mouse cursor for an Arcobjects addin tool?

2
+50

The following works for me at 10.4. I put the code in the tool's OnActivate sub. I'm not sure, but you might have to remove the cursor reference from the Config.esriaddinx file.

To use a built-in windows cursor:

MyBase.Cursor = Windows.Forms.Cursors.Cross (or whatever cursor you want to use)

To use a custom cursor add an existing .cur file to your VS solution, or create a new one. (Details here: http://www.dotnetheaven.com/article/cursors-in-vb.net) And then set the "Build Action" to Embedded Resource, and set the "Copy to Output Directory" to Copy always.

This will copy the .cur file to the assembly cache folder on client machines where the addins are unpacked.

Then in the code, to set the custom cursor, use:

MyBase.Cursor = New System.Windows.Forms.Cursor(My.MyProject.Application.Info.DirectoryPath & "\Cursor1.cur")

The addin location in the assembly cache folder is returned by My.MyProject.Application.Info.DirectoryPath, so the code will find the .cur file there.

  • Can you elaborate? What sort of "path to custom cursor" do you use? Do you store the file as a resource or something else that is included in the build? – Barbarossa Feb 27 '17 at 18:57
  • I edited the answer with some more info about the .cur file. Let me know if it needs more clarification. – Dan Jurgella Feb 27 '17 at 19:15
  • thanks for the clarification. I've been stuck on how to utilize embedded resources, but you got me in the right direction. It also helps if cursor files are 24bit color. – Barbarossa Mar 1 '17 at 4:15
1

Barbarosa, here's a sub from one of my tools that sets the cursor using a 'in memory' drawn bitmap though you could just as easily load a bitmap or cursor file from resources or known location (System.Reflection.Assembly.GetExecutingAssembly().Location perhaps). It's probably a bit too verbose but there's bound to be one or two gems that might help with your sticking point(s).

Note that the tool cursor is only valid while the mouse cursor is over the ArcMap canvas and then reverts to the current windows cursor as soon as the mouse cursor leaves the window. this.Cursor is a property of ESRI.ArcGIS.Desktop.AddIns.Tool not so for ESRI.ArcGIS.Desktop.AddIns.Button.

System.Windows.Forms.Cursor gFirstSelectionCursor;
System.Windows.Forms.Cursor gSecondSelectionCursor;
System.Windows.Forms.Cursor gSnappingCursor;

public void SetCursor()
        {
            gDispTran = gDoc.ActiveView.ScreenDisplay.DisplayTransformation;
            int pHalfCursorSize = (int)cCursorSize / 2;
            int pQuarterCursorSize = (int)pHalfCursorSize / 2;

            System.Drawing.Point UL = new System.Drawing.Point(pQuarterCursorSize, pQuarterCursorSize);
            System.Drawing.Point UR = new System.Drawing.Point(cCursorSize - pQuarterCursorSize, pQuarterCursorSize);
            System.Drawing.Point LL = new System.Drawing.Point(pQuarterCursorSize, cCursorSize - pQuarterCursorSize);
            System.Drawing.Point LR = new System.Drawing.Point(cCursorSize - pQuarterCursorSize, cCursorSize - pQuarterCursorSize);

            Bitmap pBitmap = new Bitmap(cCursorSize, cCursorSize, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            Graphics pGraphics = Graphics.FromImage(pBitmap);
            pGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            Rectangle pRect = new Rectangle(0, 0, cCursorSize, cCursorSize);
            int pHalfSelection = (int)gSelectionTolerance / 2;
            pRect = new Rectangle(pHalfCursorSize - pHalfSelection, pHalfCursorSize - pHalfSelection, gSelectionTolerance, gSelectionTolerance);
            Color pCol = Color.Transparent;

            Color pCircleColour;

            if (gCursorColour == gCursorEnum.Black)
                pCircleColour = Color.Black;
            else if (gCursorColour == gCursorEnum.Red)
                pCircleColour = Color.Red;
            else if (gCursorColour == gCursorEnum.White)
                pCircleColour = Color.White;
            else if (gCursorColour == gCursorEnum.Yellow)
                pCircleColour = Color.Yellow;
            else
                pCircleColour = Color.Black;

            // set the draw lines to the same colour as the
            // selection circle
            ILineSymbol pLineSym = new SimpleLineSymbol();
            pLineSym.Color.RGB = ((pCircleColour.R | pCircleColour.G << 8) | pCircleColour.B << 0x10);

            //pLineSym.Color = (IColor)ESRI.ArcGIS.ADF.Converter.ToRGBColor(pCircleColour);
            pLineSym.Width = 1;
            gSym = (ISymbol)pLineSym;

            Brush pBrush = new SolidBrush(pCol);
            float[] pDashValues = { 3, 1 };
            Pen pPen = new Pen(pCircleColour, 1);
            pPen.DashPattern = pDashValues;
            pGraphics.FillRectangle(pBrush, pRect);
            pGraphics.DrawEllipse(pPen, pRect);
            gFirstSelectionCursor = new System.Windows.Forms.Cursor(pBitmap.GetHicon());
            // second cursor
            pBitmap = new Bitmap(cCursorSize, cCursorSize, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            pGraphics = Graphics.FromImage(pBitmap);
            pGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            pGraphics.DrawLine(pPen, UL, LR);
            pGraphics.DrawLine(pPen, LL, UR);
            gSecondSelectionCursor = new System.Windows.Forms.Cursor(pBitmap.GetHicon());
            // third cursor
            pBitmap = new Bitmap(cCursorSize, cCursorSize, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            pGraphics = Graphics.FromImage(pBitmap);
            pGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
            pGraphics.DrawLine(pPen, UL, LR);
            pGraphics.DrawLine(pPen, LL, UR);
            pRect = new Rectangle((int)pHalfCursorSize / 2, (int)pHalfCursorSize / 2, pHalfCursorSize, pHalfCursorSize);
            pGraphics.DrawEllipse(pPen, pRect);
            gSnappingCursor = new System.Windows.Forms.Cursor(pBitmap.GetHicon());

            if (gSelectionStarted)
                this.Cursor = gSecondSelectionCursor;
            else
                this.Cursor = gFirstSelectionCursor;
        }

This defines the cursor as a circle of cCursorSize when selecting and changes to a large X when something is selected. Note that the actual cursors are global which allows for fast switching during the OnMouseDown event; this sub is only called on tool startup and when the cCursorSize is changed via a form.

1

Well I finally found a working solution after a couple days of trying, thanks to Michael Miles-Stimson, the following is a super simple answer to what I was struggling with.

I was getting that same changing icon issue and found this as the most straightforward solution. this.Cursor seems to make the difference combined with the cursors in System.Windows.Forms. I have put the following line in OnUpdate() and my tool is doing all I wanted it to.

this.Cursor = Cursors.Cross;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.