Through the Interface: Managing drag drop from a palette into AutoCAD using .NET

Kean Walmsley

May 2015

Sun Mon Tue Wed Thu Fri Sat
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30


« Back in the Bay Area | Main | Implementing drag & drop at a specific location in AutoCAD using .NET »

February 23, 2011

Managing drag & drop from a palette into AutoCAD using .NET

I’m up early (after sleeping late) with jetlag, so I thought I may as well put together this post. My brain isn’t fully functional, I suspect, so forgive any errors (but please let me know about them, so I can fix them :-).

I was inspired to implement the code in this post by an internal email thread, wherein Albert Szilvasy recommended implementing a comparable technique to a colleague. Please don’t blame Albert for the implementation details, though, they are all mine.

The idea is simple: you have some kind of modeless PaletteSet, and you want to initiate and manage a drag & drop of some kind of custom content from the palette into AutoCAD. In this case – to get started quickly – I took the code from this previous post, which displayed a palette previewing the contents of the Windows clipboard for eventual inclusion in the Clipboard Manager Plugin of the Month.

In this example we have a bitmap hosted in a palette: when someone clicks on the image, we’ll start our drag operation, and should the drag leave the palette and enter AutoCAD’s drawing window, we’ll respond to the eventual drop by saving the image in the palette as a bitmap to file and firing a command inside AutoCAD with the location of the bitmap. Once again there’s our important rule of thumb when it comes to implementing a modeless UI: rather than manually locking the current document, it’s safer to define a command – which will implicitly lock the current document – and call that from the UI via SendStringToExecute().

The custom command we call – named RINS – will simply add a raster image to the AutoCAD drawing in the current space.

Here’s the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Windows;

using System.Runtime.InteropServices;

using System.Windows.Forms;

using System.Drawing;

using System.IO;

using System;


namespace DragAndDrop


  public enum Msgs


    WM_DRAWCLIPBOARD = 0x0308,




  public class ClipboardView : UserControl



    public static extern IntPtr SetClipboardViewer(

      IntPtr hWndNewViewer



    [DllImport("user32.dll", CharSet = CharSet.Auto)]

    public static extern IntPtr SendMessage(

      IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam



    private IntPtr _nxtVwr;

    private PictureBox _img;

    private PaletteSet _ps;


    public System.Drawing.Image Contents


      get { return _img.Image; }



    public ClipboardView(PaletteSet ps)


      _img = new PictureBox();

      _img.Anchor =

        (AnchorStyles)(AnchorStyles.Top |

                      AnchorStyles.Bottom |

                      AnchorStyles.Left |


      _img.Location = new Point(0, 0);

      _img.Size = this.Size;

      _img.SizeMode = PictureBoxSizeMode.StretchImage;



      // Add our event handler to launch a new drag event

      // when someone clicks on the control


      _img.MouseDown +=

        delegate(object sender, MouseEventArgs e)


          // Simply initiate the drag & drop in AutoCAD,

          // specifying an instance of our custom "drop

          // target" class




            _img, this.Contents, DragDropEffects.Copy,

            new MyDropTarget()




      _nxtVwr = SetClipboardViewer(this.Handle);

      _ps = ps;



    private void ExtractImage()


      IDataObject iData;



        iData = Clipboard.GetDataObject();


      catch (System.Exception ex)






      if (iData.GetDataPresent("Bitmap"))


        object o = iData.GetData("Bitmap");

        Bitmap b = o as Bitmap;

        if (b != null)


          _img.Image = b;

          if (_ps != null)


            _ps.Size =

              new Size(b.Size.Width / 3, b.Size.Height / 3);






    protected override void WndProc(ref Message m)


      switch ((Msgs)m.Msg)


        case Msgs.WM_DRAWCLIPBOARD:


          SendMessage(_nxtVwr, m.Msg, m.WParam, m.LParam);


        case Msgs.WM_CHANGECBCHAIN:

          if (m.WParam == _nxtVwr)

            _nxtVwr = m.LParam;


            SendMessage(_nxtVwr, m.Msg, m.WParam, m.LParam);



          base.WndProc(ref m);






  // Our custom drop target class


  public class MyDropTarget : Autodesk.AutoCAD.Windows.DropTarget


    public override void OnDrop(DragEventArgs e)


      Document doc =




      // If we have a valid bitmap, save it and send

      // our custom command (RINS)


      if (e.Data.GetDataPresent("Bitmap"))


        object o = e.Data.GetData("Bitmap");

        Bitmap b = o as Bitmap;

        if (b != null)


          // We'll save the bitmap as a "temp" file

          // (as its unique - a better approach

          // would probably to create a unique file

          // in the same folder as the drawing, but

          // that's left to the reader)


          string path = Path.GetTempFileName();



          // Call our command


          string cmd = "_RINS " + path + "\n";


            cmd, false, false, false







  public class Commands


    private PaletteSet _ps = null;

    private ClipboardView _cv = null;

    static private Point3d _pt = Point3d.Origin;



    public void DragAndDropClipboardRaster()


      if (_ps == null)


        _ps = new PaletteSet(


          new Guid("5C8FC28C-45ED-4796-BD40-28D235B6D7DA")



        if (_cv == null)


          _cv = new ClipboardView(_ps);



        _ps.Text = "Clipboard";

        _ps.DockEnabled =

          DockSides.Left | DockSides.Right | DockSides.None;

        _ps.Size = new System.Drawing.Size(300, 500);

        _ps.Add("ClipboardView", _cv);


      _ps.Visible = true;




    // Our custom command to insert a raster image



    public void RasterInsert()


      Document doc =



      Database db = doc.Database;

      Editor ed = doc.Editor;


      // Pick up the location of the bitmap image


      PromptStringOptions pso =

        new PromptStringOptions(

          "\nPath of raster image: "


      pso.AllowSpaces = true;

      PromptResult pr = ed.GetString(pso);

      if (pr.Status != PromptStatus.OK)



      if (!File.Exists(pr.StringResult))



          "\nFile does not exist."





      string path = pr.StringResult;


      Transaction tr =


      using (tr)


        // Get or create our raster image dictionary


        ObjectId dictId =


        if (dictId == ObjectId.Null)

          dictId = RasterImageDef.CreateImageDictionary(db);


        // And open it for write


        DBDictionary dict =


            dictId, OpenMode.ForWrite



        // Get a unique name for our definition object


        string name = RasterImageDef.SuggestName(dict, path);


        // Create the definition and add it to the dictionary


        RasterImageDef rid = new RasterImageDef();

        rid.SourceFileName = path;


        ObjectId ridId = dict.SetAt(name, rid);

        tr.AddNewlyCreatedDBObject(rid, true);


        // Now we'll add our raster image reference

        // to the current space


        BlockTableRecord btr =






        // Create and add the image reference


        RasterImage ri = new RasterImage();

        ri.ImageDefId = ridId;



        tr.AddNewlyCreatedDBObject(ri, true);


        // Of course we commit







Here’s what happens when we run the DRAGDROP command and copy a block to the clipboard:

Our clipboard-viewing palette next to the copied geometry

As we click on the image to start the drag, we see the “not allowed” cursor:

Starting the drag

As we drag onto the drawing canvas, we see the “drag a copy” cursor:

During the drag

And as we drop we see the RINS command execute and our raster image get inserted at the origin of the drawing with the default size:

After the drop

Where in this post we’re just passing the location (on disk) of the newly-saved image to the RINS command, in the next post I intend to extend the command to take the location of the drop as being the insertion point of the image and then prompt the user for the second corner. We’ll probably use the raster image jig from this previous post to help with that.

One problem I’m currently having (hopefully due to jetlag-related fogginess) is with translating the screen coordinates provided to the the drop target into a position inside AutoCAD (UCS or WCS, I don’t much care) at which we can create the raster image reference. If anyone knows a simple way to get from screen coordinates to some kind of drawing coordinates, please let me know. I’ve tried a point monitor, but that unfortunately only gets the cursor’s position in the drawing prior to entering the palette, rather than at the drop location.

blog comments powered by Disqus


10 Random Posts