When we do our customizing programming with AutoCAD's .NET API, we can easily create Drag & Drop feature into our custom applications. Imagine this user case: a form presenting some drawing options, such as drawing circles with different radius; user is asked to drag one option into AutoCAD editor to actually have the circle drawn.
Here is an entire set of code to do this.
The form that presents circle drawing options looks like this:
The code behind the form is as following:
1 using System;
2 using System.Linq;
3 using System.Windows.Forms;
4
5 namespace DragDropIntoAcad
6 {
7 public partial class dlgCircle : Form
8 {
9 public dlgCircle()
10 {
11 InitializeComponent();
12 }
13
14 private void LoadCircleList()
15 {
16 string[] circles = new string[]
17 {
18 "Radius=3",
19 "Radius=4",
20 "Radius=5",
21 "Radius=6",
22 "Radius=7",
23 "Radius=8",
24 "Radius=9",
25 "Radius=10",
26 "Radius=11",
27 "Radius=12"
28 };
29
30 var data = from c in circles
31 select new {
32 CircleName = c,
33 CircleRadius = Int32.Parse(c.Substring(7))
34 };
35
36 cboCircle.DisplayMember = "CircleName";
37 cboCircle.ValueMember = "CircleRadius";
38 cboCircle.DataSource = data.ToList();
39 }
40
41 private void dlgCircle_Load(object sender, EventArgs e)
42 {
43 LoadCircleList();
44 }
45
46 private void btnClose_Click(object sender, EventArgs e)
47 {
48 this.Visible = false;
49 }
50
51 private void dlgCircle_FormClosing(
52 object sender, FormClosingEventArgs e)
53 {
54 e.Cancel = true;
55 this.Visible = false;
56 }
57
58 private void lblCircle_MouseDown(object sender, MouseEventArgs e)
59 {
60 if (e.Button == MouseButtons.Left)
61 {
62 double r = Convert.ToDouble(cboCircle.SelectedValue);
63
64 CircleDropper.DragDropCircle(lblCircle, r);
65 }
66 }
67 }
68 }
There are 2 things in the form's code to pay attention.
Firstly, I intend to show the form as modeless form, thus clicking the "Close" button, or clicking the "x" button of the form will only hide the form. In the command method where the form is called to show, the code there make sure only one instance of the form is ever created.
Secondly, the whole trick of doing "Drag & Drop" here lies in lblCircle_MouseDown() event handler. In order to separate AutoCAD functioning code with the UI code (the form code), I create a static class CircleDropper, which hides all the AutoCAD operation (of drawing circle) details away from the UI.
Here is the code for class CircleDropper, which draws the circle when something dragged from the form and dropped into AutoCAD editor:
1 using System.Windows.Forms;
2 using Autodesk.AutoCAD.ApplicationServices;
3 using Autodesk.AutoCAD.DatabaseServices;
4 using Autodesk.AutoCAD.EditorInput;
5 using Autodesk.AutoCAD.Geometry;
6 using Autodesk.AutoCAD.Windows;
7
8 namespace DragDropIntoAcad
9 {
10 public static class CircleDropper
11 {
12 public static void DragDropCircle(
13 System.Windows.Forms.Control ctl, double radius)
14 {
15 //Complete Drag & Drop operation, which mainly for getting
16 //a point indicating where the dropping occurs
17 CircleDropTarget dropTarget = new CircleDropTarget();
18 Autodesk.AutoCAD.ApplicationServices.Application.
19 DoDragDrop(ctl, radius, DragDropEffects.Copy, dropTarget);
20
21 Document dwg=Autodesk.AutoCAD.ApplicationServices.
22 Application.DocumentManager.MdiActiveDocument;
23 Editor ed=dwg.Editor;
24
25 Autodesk.AutoCAD.Internal.Utils.SetFocusToDwgView();
26
27 //Create a circle. and then start an EntityJig to give user
28 //a chance to accurately position the circle, or change
29 //the circle's radius, or even cancel the operation
30 using (dwg.LockDocument())
31 {
32 using (Circle c = new Circle())
33 {
34 c.Radius = radius;
35
36 //Set circle's centre at location where mouse drag-drops at
37 c.Center = dropTarget.DropPoint;
38
39 CircleJig jig = new CircleJig(ed, c);
40 if (jig.Drag())
41 {
42 //Add the circle into drawing database
43 using (Transaction tran =
44 dwg.TransactionManager.StartTransaction())
45 {
46 BlockTableRecord space = (BlockTableRecord)
47 tran.GetObject(dwg.Database.CurrentSpaceId,
48 OpenMode.ForWrite);
49
50 space.AppendEntity(c);
51 tran.AddNewlyCreatedDBObject(c, true);
52
53 tran.Commit();
54 }
55 }
56 }
57 }
58
59 Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
60 }
61 }
62
63 public class CircleDropTarget : DropTarget
64 {
65 private Point3d _dropPoint = Point3d.Origin;
66
67 public Point3d DropPoint
68 {
69 get { return _dropPoint; }
70 }
71
72 public override void OnDrop(System.Windows.Forms.DragEventArgs e)
73 {
74 //Convert windows location of mouse into AutoCAD editor's
75 //WCS coordinate (Point3d)
76 Document dwg = Autodesk.AutoCAD.ApplicationServices.
77 Application.DocumentManager.MdiActiveDocument;
78
79 System.Drawing.Point pt = new System.Drawing.Point(e.X, e.Y);
80 _dropPoint = dwg.Editor.PointToWorld(pt);
81 }
82 }
83
84 public class CircleJig : EntityJig
85 {
86 private enum DragFor
87 {
88 Location=0,
89 Radius=1,
90 }
91
92 private Editor _ed;
93 private DragFor _dragFor = DragFor.Location;
94 private Point3d _basePoint;
95
96 private Point3d _currCentre;
97 private double _currRadius;
98 private Circle _circle;
99
100 public CircleJig(Editor ed, Circle circle):base(circle)
101 {
102 _ed = ed;
103 _circle = (Circle)this.Entity;
104
105 _basePoint = _circle.Center;
106 }
107
108 public bool Drag()
109 {
110 while (true)
111 {
112 _basePoint = _circle.Center;
113
114 _currCentre = _circle.Center;
115 _currRadius = _circle.Radius;
116
117 PromptResult res = _ed.Drag(this);
118 if (res.Status == PromptStatus.OK)
119 {
120 return true;
121 }
122 else if (res.Status == PromptStatus.Keyword)
123 {
124 switch(res.StringResult.ToUpper())
125 {
126 case "LOCATION":
127 _dragFor = DragFor.Location;
128 break;
129 case "RADIUS":
130 _dragFor = DragFor.Radius;
131 break;
132 default:
133 return false;
134 }
135 }
136 else
137 {
138 return false;
139 }
140 }
141 }
142
143 protected override bool Update()
144 {
145 return true;
146 }
147
148 protected override SamplerStatus Sampler(JigPrompts prompts)
149 {
150 SamplerStatus status;
151
152 switch (_dragFor)
153 {
154 case DragFor.Location:
155 status = CircleLocationSampler(prompts);
156 break;
157 case DragFor.Radius:
158 status = CircleRadiusSampler(prompts);
159 break;
160 default:
161 status = SamplerStatus.NoChange;
162 break;
163 }
164
165 return status;
166 }
167
168 protected SamplerStatus CircleLocationSampler(JigPrompts prompts)
169 {
170 SamplerStatus status = SamplerStatus.NoChange;
171
172 JigPromptPointOptions opt = new JigPromptPointOptions(
173 "\nPick circle centre point:");
174 opt.AppendKeywordsToMessage = true;
175 opt.UseBasePoint = true;
176 opt.BasePoint = _basePoint;
177 opt.Cursor = CursorType.RubberBand;
178 opt.UserInputControls = UserInputControls.NullResponseAccepted;
179 opt.Keywords.Add("Radius");
180 opt.Keywords.Add("Cancel");
181 opt.Keywords.Default = "Cancel";
182
183 PromptPointResult res = prompts.AcquirePoint(opt);
184 if (res.Status == PromptStatus.OK)
185 {
186 _currCentre = res.Value;
187 if (_currCentre != _circle.Center)
188 {
189 ChangeCircleLocation();
190 status = SamplerStatus.OK;
191 }
192 }
193 else
194 {
195 status = SamplerStatus.Cancel;
196 }
197
198 return status;
199 }
200
201 protected SamplerStatus CircleRadiusSampler(JigPrompts prompts)
202 {
203 SamplerStatus status = SamplerStatus.NoChange;
204
205 JigPromptDistanceOptions opt = new JigPromptDistanceOptions(
206 "\nPick/enter circle radius:");
207 opt.AppendKeywordsToMessage = true;
208 opt.UseBasePoint = true;
209 opt.BasePoint = _basePoint;
210 opt.Cursor = CursorType.RubberBand;
211 opt.UserInputControls =
212 UserInputControls.NullResponseAccepted |
213 UserInputControls.NoZeroResponseAccepted |
214 UserInputControls.NoNegativeResponseAccepted;
215 opt.Keywords.Add("Location");
216 opt.Keywords.Add("Cancel");
217 opt.Keywords.Default = "Cancel";
218
219 PromptDoubleResult res = prompts.AcquireDistance(opt);
220 if (res.Status == PromptStatus.OK)
221 {
222 _currRadius = res.Value;
223 if (_currRadius != _circle.Radius)
224 {
225 ChangeCircleRadius();
226 status = SamplerStatus.OK;
227 }
228 }
229 else
230 {
231 status = SamplerStatus.Cancel;
232 }
233
234 return status;
235 }
236
237 private void ChangeCircleLocation()
238 {
239 Matrix3d mt = Matrix3d.Displacement(
240 _circle.Center.GetVectorTo(_currCentre));
241 _circle.TransformBy(mt);
242 }
243
244 private void ChangeCircleRadius()
245 {
246 _circle.Radius = _currRadius;
247 }
248 }
249 }
From the code we can see, Autodesk.AutoCAD.ApplicationServices.Application.DoDragDrop() is the key method that gets some information on what and where something is dropped into AutoCAD, which needs an argument of Autodesk.AutoCAD.Windows.DropTarget type. Since DropTarget is an abstract class, we must derive our own custom DropTarget class, hence the CircleDropTarget class here.
In most cases, only OnDrop() method in the DropTarget class is needed to be overridden, and the primary goal of that method is to obtain a point where the dropping occurs. While it is doable that in this method we add code to let AutoCAD actually draw what we want (circle, in my case), it is desired to separate entity generating code from this OnDrop() method, as my code shows.
It is also obvious that when dragging something into AutoCAD, the mouse cursor location is not ideal to accurate drafting/CAD operation. It would be good practice that once the mouse cursor is dragged into AutoCAD and dropped, user is asked to select a point in standard AutoCAD manner, better yet, user is given a sort of Jig to dynamically pick or enter accurate location, and the change for other possible operation options, including cancelling the Drag & Drop operation. All AutoCAD users know when a drawing file is dragged from Windows Explorer and dropped into AutoCAD starts "INSERT" command with a block inserting jig. This the reason of my code using an EntityJig after Drag & Drop.
OK, here is the last piece of code that runs the code:
1 using Autodesk.AutoCAD.ApplicationServices;
2 using Autodesk.AutoCAD.EditorInput;
3 using Autodesk.AutoCAD.Runtime;
4
5 [assembly: CommandClass(typeof(DragDropIntoAcad.MyCommands))]
6
7 namespace DragDropIntoAcad
8 {
9 public class MyCommands
10 {
11 private static dlgCircle dlg=null;
12
13 [CommandMethod("DragDrop")]
14 public static void RunMyCommand()
15 {
16 Document dwg = Application.DocumentManager.MdiActiveDocument;
17 Editor ed = dwg.Editor;
18
19 if (dlg == null)
20 {
21 dlg = new dlgCircle();
22 }
23
24 Application.ShowModelessDialog(dlg);
25
26 Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
27 }
28 }
29 }
As usual, this video clip shows how the code works.
Personally, I do not there is much difference between dragging something from a form/tool palette into AutoCAD and clicking a ribbon/menu/toolbar item in terms of creating new entity. But, hey, if you use it properly in your custom application, your user may like it.