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.
4 comments:
Hi Norman, love the code but I'm having trouble with the PointToScreen and PointToWorld functions in your EditorSelectionExtension.
They're both looking for System.Windows.Point and you're using System.Drawing.Point. Furthermore, I can't even reference System.Windows.Point in my winforms application correctly.
What am I missing?
The method Editor.PointToWorld() was broken in AutoCAD 2013 or 2014.
So, you did not miss anything. You just need to pass in a System.Windows.Point, instead of System.Drawing.Point.
You need to add reference to System.WindowsBae to your project
Thanks for the help. I managed to get it working.
Post a Comment