Thanks for Mike Schumacher for pointing me at Editor.PointToWorld() (I’m not sure how I managed to miss it – let’s blame it on the jetlag).
1 using Autodesk.AutoCAD.ApplicationServices;
2 using Autodesk.AutoCAD.DatabaseServices;
3 using Autodesk.AutoCAD.EditorInput;
4 using Autodesk.AutoCAD.Geometry;
5 using Autodesk.AutoCAD.Runtime;
6 using Autodesk.AutoCAD.Windows;
7 using System.Runtime.InteropServices;
8 using System.Windows.Forms;
9 using System.Drawing;
10 using System.IO;
11 using System;
12
13 namespace DragAndDrop
14 {
15 public enum Msgs
16 {
17 WM_DRAWCLIPBOARD = 0x0308,
18 WM_CHANGECBCHAIN = 0x030D
19 }
20
21 public class ClipboardView : UserControl
22 {
23 [DllImport("user32.dll")]
24 public static extern IntPtr SetClipboardViewer(
25 IntPtr hWndNewViewer
26 );
27
28 [DllImport("user32.dll", CharSet = CharSet.Auto)]
29 public static extern IntPtr SendMessage(
30 IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam
31 );
32
33 private IntPtr _nxtVwr;
34 private PictureBox _img;
35 private PaletteSet _ps;
36
37 public System.Drawing.Image Contents
38 {
39 get { return _img.Image; }
40 }
41
42 public ClipboardView(PaletteSet ps)
43 {
44 _img = new PictureBox();
45 _img.Anchor =
46 (AnchorStyles)(AnchorStyles.Top |
47 AnchorStyles.Bottom |
48 AnchorStyles.Left |
49 AnchorStyles.Right);
50 _img.Location = new Point(0, 0);
51 _img.Size = this.Size;
52 _img.SizeMode = PictureBoxSizeMode.StretchImage;
53 Controls.Add(_img);
54
55 // Add our event handler to launch a new drag event
56 // when someone clicks on the control
57
58 _img.MouseDown +=
59 delegate(object sender, MouseEventArgs e)
60 {
61 // Simply initiate the drag & drop in AutoCAD,
62 // specifying an instance of our custom "drop
63 // target" class
64
65 Autodesk.AutoCAD.ApplicationServices.
66 Application.DoDragDrop(
67 _img, this.Contents, DragDropEffects.Copy,
68 new MyDropTarget()
69 );
70 };
71
72 _nxtVwr = SetClipboardViewer(this.Handle);
73 _ps = ps;
74 }
75
76 private void ExtractImage()
77 {
78 IDataObject iData;
79 try
80 {
81 iData = Clipboard.GetDataObject();
82 }
83 catch (System.Exception ex)
84 {
85 MessageBox.Show(ex.ToString());
86 return;
87 }
88
89 if (iData.GetDataPresent("Bitmap"))
90 {
91 object o = iData.GetData("Bitmap");
92 Bitmap b = o as Bitmap;
93 if (b != null)
94 {
95 _img.Image = b;
96 if (_ps != null)
97 {
98 _ps.Size =
99 new Size(b.Size.Width / 3, b.Size.Height / 3);
100 }
101 }
102 }
103 }
104
105 protected override void WndProc(ref Message m)
106 {
107 switch ((Msgs)m.Msg)
108 {
109 case Msgs.WM_DRAWCLIPBOARD:
110 ExtractImage();
111 SendMessage(_nxtVwr, m.Msg, m.WParam, m.LParam);
112 break;
113 case Msgs.WM_CHANGECBCHAIN:
114 if (m.WParam == _nxtVwr)
115 _nxtVwr = m.LParam;
116 else
117 SendMessage(_nxtVwr, m.Msg, m.WParam, m.LParam);
118 break;
119 default:
120 base.WndProc(ref m);
121 break;
122 }
123 }
124 }
125
126 // Our custom drop target class
127
128 public class MyDropTarget : DropTarget
129 {
130 public override void OnDrop(DragEventArgs e)
131 {
132 Document doc =
133 Autodesk.AutoCAD.ApplicationServices.Application.
134 DocumentManager.MdiActiveDocument;
135
136 // If we have a valid bitmap, save it and send
137 // our custom command (RINS)
138
139 if (e.Data.GetDataPresent("Bitmap"))
140 {
141 object o = e.Data.GetData("Bitmap");
142 Bitmap b = o as Bitmap;
143 if (b != null)
144 {
145 // We'll save the bitmap as a "temp" file
146 // (as its unique - a better approach
147 // would probably to create a unique file
148 // in the same folder as the drawing, but
149 // that's left to the reader)
150
151 string path = Path.GetTempFileName();
152 b.Save(path);
153
154 // Call our command
155
156 string cmd =
157 String.Format(
158 "_RINS {0}\n{1},{2},0\n", path, e.X, e.Y
159 );
160 doc.SendStringToExecute(
161 cmd, false, false, false
162 );
163 }
164 }
165 }
166 }
167
168 public class Commands
169 {
170 private PaletteSet _ps = null;
171 private ClipboardView _cv = null;
172 static private Point3d _pt = Point3d.Origin;
173
174 [CommandMethod("DRAGDROP")]
175 public void DragAndDropClipboardRaster()
176 {
177 if (_ps == null)
178 {
179 _ps = new PaletteSet(
180 "DRAGDROP",
181 new Guid("5C8FC28C-45ED-4796-BD40-28D235B6D7DA")
182 );
183
184 if (_cv == null)
185 {
186 _cv = new ClipboardView(_ps);
187 }
188
189 _ps.Text = "Clipboard";
190 _ps.DockEnabled =
191 DockSides.Left | DockSides.Right | DockSides.None;
192 _ps.Size = new System.Drawing.Size(300, 500);
193 _ps.Add("ClipboardView", _cv);
194 }
195 _ps.Visible = true;
196 }
197
198
199 // Our custom command to insert a raster image
200
201 [CommandMethod("RINS")]
202 public void RasterInsert()
203 {
204 Document doc =
205 Autodesk.AutoCAD.ApplicationServices.Application.
206 DocumentManager.MdiActiveDocument;
207 Database db = doc.Database;
208 Editor ed = doc.Editor;
209
210 // Pick up the location of the bitmap image
211
212 PromptStringOptions pso =
213 new PromptStringOptions(
214 "\nPath of raster image: "
215 );
216 pso.AllowSpaces = true;
217 PromptResult pr = ed.GetString(pso);
218 if (pr.Status != PromptStatus.OK)
219 return;
220
221 if (!File.Exists(pr.StringResult))
222 {
223 ed.WriteMessage(
224 "\nFile does not exist."
225 );
226 return;
227 }
228
229 string path = pr.StringResult;
230
231 // Pick up the position of the drop in screen coords
232
233 PromptPointOptions ppo =
234 new PromptPointOptions(
235 "\nPosition of raster image: "
236 );
237 PromptPointResult ppr = ed.GetPoint(ppo);
238 if (pr.Status != PromptStatus.OK)
239 return;
240
241 Point3d pos = ppr.Value;
242
243 // Calculate the displacement vector
244
245 Point3d pt =
246 ed.PointToWorld(new Point((int)pos.X, (int)pos.Y));
247 Matrix3d disp =
248 Matrix3d.Displacement(pt - Point3d.Origin);
249
250 Transaction tr =
251 db.TransactionManager.StartTransaction();
252 using (tr)
253 {
254 // Get or create our raster image dictionary
255
256 ObjectId dictId =
257 RasterImageDef.GetImageDictionary(db);
258 if (dictId == ObjectId.Null)
259 dictId = RasterImageDef.CreateImageDictionary(db);
260
261 // And open it for write
262
263 DBDictionary dict =
264 (DBDictionary)tr.GetObject(
265 dictId, OpenMode.ForWrite
266 );
267
268 // Get a unique name for our definition object
269
270 string name = RasterImageDef.SuggestName(dict, path);
271
272 // Create the definition and add it to the dictionary
273
274 RasterImageDef rid = new RasterImageDef();
275 rid.SourceFileName = path;
276 rid.Load();
277 ObjectId ridId = dict.SetAt(name, rid);
278 tr.AddNewlyCreatedDBObject(rid, true);
279
280 // Now we'll add our raster image reference
281 // to the current space
282
283 BlockTableRecord btr =
284 (BlockTableRecord)tr.GetObject(
285 db.CurrentSpaceId,
286 OpenMode.ForWrite
287 );
288
289 // Create and add the image reference
290
291 RasterImage ri = new RasterImage();
292 ri.ImageDefId = ridId;
293 ri.SetClipBoundaryToWholeImage();
294 btr.AppendEntity(ri);
295 tr.AddNewlyCreatedDBObject(ri, true);
296 ri.TransformBy(disp);
297
298 // Of course we commit
299
300 tr.Commit();
301 }
302 }
303 }
304 }
The changes, as you can see, are very modest: the SendStringToExecute() call now sends the point in screen coordinates (adding a zero for Z, just to simplify collection by the command). The command collects and transforms the point before creating a displacement vector to apply to the raster image reference. The matrix gets applied to the image after it has been added to the drawing, but we could very well do so beforehand.
The code now allows the user to drop images from our palette at specific locations in the drawing:
I’m heading off to Las Vegas in the morning, but will hopefully still have time to post the jig update at the beginning of next week.