Thursday, March 14, 2013

Update - Custom Double-Click Action Using Application.BeginDoubleClick Event

I posted an article a few months ago on the topic of using Application.BeginDoubleClick event to define our own custom double-click action here.

There a discussion in Autodesk's .NET forum revealed that there is an issue which causes my solution does not work: if the double clicked entity is a BlockReference with attributes in it, the Editor.SelecctImplied() method returns PromptStatus.Error.

I tend to think this is a bug in AutoCAD managed API. Whether it is a bug or not, this is serious blow to the logic of my solution of doing custom double-click action, because BlockReference with attributes in it could be the most targeting entity for custom double-click action when doing AutoCAD custom programming.

Also, I found my logic in that post may not be good enough. For example, user can click any entity or entities to get it or them highlighted and then double-click anywhere in the Editor to get the code in my Application.BeginDoubleClick event handler run and trigger my custom double-click action, because in this case, Editor.SelectImplied() still can get "pickfirst" selection set. This may not be user wants.

So, in order to find workaround for the Blockreference with attributes not working issue and make the custom double-click action more accurate on what entity user is actually double-clicked, I worked out a solution that does not use Editor.SelectImplied() in Application.BeginDoubleClick event handler to find out exactly what entity is double-clicked and then do things accordingly (e.g. trigger my custom double-click action if needed).

The logic is like this:

In the Application.BeginDoubleClick event handler, I get current mouse location, and then get the cursor (the pickbox)'s size. Then I use the pickbox' information to get 4 points representing the 4 corners of the pickbox of the cursor when double-clicking occurs. Once the 4 points are available, I use Editor.SelectCrossingPolygon() method to find entity or entities being double-clicked.

This approach is obviously more accurately reflect what user exactly double-clicked on. Depending on the pickbox's size (set by system variable "PICKBOX", value from 0 to 20 pixel), none (when ""PICKBOX" is set to 0) or more entities could be double-clicked.

With this logic in mind, I created and Editor extension method to return a PromptSelectionResult object, which takes a Point3d input (the cursor's location where double-click occurs):

    1 public static class EditorSelectionExtension
    2 {
    3     public static PromptSelectionResult  SelectAtPickBox(
    4         this Editor ed, Point3d pickBoxCentre)
    5     {
    6         //Get pick box's size on screen
    7         System.Drawing.Point screenPt = ed.PointToScreen(pickBoxCentre, 1);
    8 
    9         //Get pickbox's size. Note, the number obtained from
   10         //system variable "PICKBOX" is actually the half of
   11         //pickbox's width/height
   12         object pBox = Application.GetSystemVariable("PICKBOX");
   13         int pSize = Convert.ToInt32(pBox);
   14 
   15         //Define a Point3dCollection for CrossingWindow selecting
   16         Point3dCollection points = new Point3dCollection();
   17 
   18         System.Drawing.Point p;
   19         Point3d pt;
   20 
   21         p = new System.Drawing.Point(screenPt.X - pSize, screenPt.Y - pSize);
   22         pt = ed.PointToWorld(p, 1);
   23         points.Add(pt);
   24 
   25         p = new System.Drawing.Point(screenPt.X + pSize, screenPt.Y - pSize);
   26         pt = ed.PointToWorld(p, 1);
   27         points.Add(pt);
   28 
   29         p = new System.Drawing.Point(screenPt.X + pSize, screenPt.Y + pSize);
   30         pt = ed.PointToWorld(p, 1);
   31         points.Add(pt);
   32 
   33         p = new System.Drawing.Point(screenPt.X - pSize, screenPt.Y + pSize);
   34         pt = ed.PointToWorld(p, 1);
   35         points.Add(pt);
   36 
   37         return ed.SelectCrossingPolygon(points);
   38     }
   39 }

With this extension method available, I simply modified the portion of code for getting selected entity or entities in Application_BeginDoubeClick() event handler of my original solution. Now, my solution for custom double-click action work as good as the original one on every entity type, including BlockReference with attributes, of course. Here is the original code in the Application_BeginDoubleClick() event handler:

    1 private static void Application_BeginDoubleClick(
    2     object sender, BeginDoubleClickEventArgs e)
    3 {
    4     _customCmd = null;
    5     _selectedEntId = ObjectId.Null;
    6 
    7     //Get entity which user double-clicked on
    8     Editor ed=Application.DocumentManager.MdiActiveDocument.Editor;
    9     PromptSelectionResult res = ed.SelectImplied();
   10     if (res.Status == PromptStatus.OK)
   11     {
   12         ObjectId[] ids = res.Value.GetObjectIds();
   13 
   14         //Only when there is one entity selected, we go ahead to see
   15         //if there is a custom command supposed to target at this entity
   16         if (ids.Length == 1)
   17         {
   18             //Find mapped custom command name
   19             string cmd = _customCommands.GetCustomCommand(ids[0]);
   20             if (!string.IsNullOrEmpty(cmd))
   21             {
   22                 _selectedEntId = ids[0];
   23                 _customCmd = cmd;
   24 
   25                 ed.WriteMessage("\nRun command {0} agianst entity {1}",
   26                     _customCmd, _selectedEntId.ToString());
   27 
   28                 if (System.Convert.ToInt32(
   29                     Application.GetSystemVariable("DBLCLKEDIT")) == 0)
   30                 {
   31                     //Since "Double click editing" is not enabled, we'll
   32                     //go ahead to launch our custom command
   33                     LaunchCustomCommand(ed);
   34                 }
   35                 else
   36                 {
   37                     //Since "Double Click Editing" is enabled, a command
   38                     //defined in CUI/CUIX will be fired. Let the code return
   39                     //and wait the DocumentLockModeChanged and
   40                     //DocumentLockModeChangeVetoed event handlers do their job
   41                     return;
   42                 }
   43             }
   44             else
   45             {
   46                 ed.WriteMessage(
   47                     "\nNo custom command is defined agaist the selected entity.");
   48             }
   49         }
   50     }
   51     else
   52     {
   53         ed.WriteMessage("\nNo entity or more than 1 entities selected.");
   54     }
   55 }

Here is updated code in the Application_BeginDoubleClick event handler:

    1 private static void Application_BeginDoubleClick(
    2     object sender, BeginDoubleClickEventArgs e)
    3 {
    4     _customCmd = null;
    5     _selectedEntId = ObjectId.Null;
    6 
    7     //Get entity which user double-clicked on
    8     Editor ed=Application.DocumentManager.MdiActiveDocument.Editor;
    9     PromptSelectionResult res = ed.SelectAtPickBox(e.Location);
   10     if (res.Status == PromptStatus.OK)
   11     {
   12         ObjectId[] ids = res.Value.GetObjectIds();
   13 
   14         //Only when there is one entity selected, we go ahead to see
   15         //if there is a custom command supposed to target at this entity
   16         if (ids.Length == 1)
   17         {
   18             //Find mapped custom command name
   19             string cmd = _customCommands.GetCustomCommand(ids[0]);
   20             if (!string.IsNullOrEmpty(cmd))
   21             {
   22                 _selectedEntId = ids[0];
   23                 _customCmd = cmd;
   24 
   25                 ed.WriteMessage("\nRun command {0} agianst entity {1}",
   26                     _customCmd, _selectedEntId.ToString());
   27 
   28                 if (System.Convert.ToInt32(
   29                     Application.GetSystemVariable("DBLCLKEDIT")) == 0)
   30                 {
   31                     //Since "Double click editing" is not enabled, we'll
   32                     //go ahead to launch our custom command
   33                     LaunchCustomCommand(ed);
   34                 }
   35                 else
   36                 {
   37                     //Since "Double Click Editing" is enabled, a command
   38                     //defined in CUI/CUIX will be fired. Let the code return
   39                     //and wait the DocumentLockModeChanged and
   40                     //DocumentLockModeChangeVetoed event handlers do their job
   41                     return;
   42                 }
   43             }
   44             else
   45             {
   46                 ed.WriteMessage(
   47                     "\nNo custom command is defined agaist the selected entity.");
   48             }
   49         }
   50     }
   51     else
   52     {
   53         ed.WriteMessage("\nNo entity or more than 1 entities selected.");
   54     }
   55 }

As you can see, once the extension method Editor.SelectAtPickBox() is available, I only changed one line of the code in the original solution.

Now my custom double-click action by-passes the possible AutoCAD .NET API bug that prevents BlockReference with attribute from being selected by Editor.SelectImplied().

To see the whole solution of custom double-click action, go the link provided at the beginning of the article.

Followers

About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.