Wednesday, July 29, 2026

A Workaround To The Drag&Drop API Operation in AutoCAD 2027/.NET 10.0

There is a recent discussion in Autodesk's AutoCAD .NET API discussion forum on the topic of broken Drag&Drop operation API in AutoCAD 2027 due to the change from .NET 8.0 to .NET 10.0. The root cause of the issue is the changed .NET support to System.Windows.Forms.IData interface in .NET 8.0 (including older .NET Core/Framework versions) and .NET 10.0. IData interface is used in the Drag$Drop operation as the data transfer "middle man" between the source (where the data is dragged) and the target (where the data is dropped). 

In a scenario like this: on a WinForm UI, user can enter radius and center point of a circle; then user can drag the data and drop onto AutoCAD's editor to have the circle drawn. As programmer, we need to derive a custom class from Autodesk.AutoCAD.Windows.DropTarget class to enable our custom data can be passed from the UI to AutoCAD when Application.DoDragDrop() method is called.

Bellow is the code of the Drag&Drop operation.

1. The WinForm UI:


2. The UI's code-behind:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
 
namespace DragAndDrop2027
{
    public partial class DrawForm : Form
    {
        private Point3d _center = new Point3d(1000, 1000, 0);
        public DrawForm()
        {
            InitializeComponent();
            SetCenterTextBoxes();
        }
 
        private void CircleLabel_MouseMove(object senderMouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                try
                {
                    var radius = Convert.ToDouble(RadiusTextBox.Text);
                    CircleDropper.DropCircleEntity(thisradius, _center, CadHelper.CreateCircle);
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show($"Error:\n{ex.Message}");
                }
            }
        }
 
        private void PickPointButton_Click(object senderEventArgs e)
        {
            try
            {
                this.Visible = false;
                if (CadHelper.TrySelectCenterPoint(out Point3d center))
                {
                    _center = centerSetCenterTextBoxes();
                    SetCenterTextBoxes();
                }
            }
            finally
            {
                this.Visible = true;
            }
        }
 
        private void SetCenterTextBoxes()
        {
            XTextBox.Text = $"{_center.X}";
            YTextBox.Text = $"{_center.Y}";
            ZTextBox.Text = $"{_center.Z}";
        }
 
        private void DrawForm_FormClosing(object senderFormClosingEventArgs e)
        {
            e.Cancel = true;
            Visible = false;
        }
    }
}
 
3. Custom class for Drag&Drop operation:
using Autodesk.AutoCAD.Windows;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace DragAndDrop2026
{
    public class CircleInfo
    {
        public double Radius { getset; }
        public Point3d Center { getset; }
    }
 
    public class CircleDropper
    {
        public static ObjectId DropCircleEntity(
            System.Windows.Forms.Form userUi,
            double radiusPoint3d center,
            Func<DocumentPoint3ddoubleObjectIdentityCreation)
        {
            var target = new CircleDropTarget(entityCreation);
 
            CircleInfo circle = new() { Radius = radius, Center = center };
            IDataObject dataObject = new DataObject();
            dataObject.SetData(typeof(CircleInfo), circle);
 
            CadApp.DoDragDrop(userUidataObjectDragDropEffects.Copy, target);
 
            return target.EntityId;
        }
    }
 
    public class CircleDropTarget : DropTarget
    {
        private readonly Func<DocumentPoint3ddoubleObjectId> _createFunction;
 
        public CircleDropTarget(Func<DocumentPoint3ddoubleObjectIdentityCreation)
        {
            _createFunction = entityCreation;
        }
 
        public ObjectId EntityId { private setget; } = ObjectId.Null;
        public override void OnDrop(DragEventArgs e)
        {
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
 
            // Get data passed from Application.DoDragDrop()
            // With .NET 10.0, this GetData(Type) method woul return null
            // if the Type argument is a object Type. This is where this
            // Drag&Drop operation fails
            var data = e.Data.GetData(typeof(CircleInfo));
 
            // The following code would do nothing because the data passed from
            // Application.DoDragDrop() is null, or raise exception if the "data" is
            // not tested for being null
            if (data != null)
            {
                var circle = (CircleInfo)data;
                //Create the entity
                EntityId = _createFunction(dwgcircle.Center, circle.Radius);
 
                CadApp.MainWindow.Focus();
                dwg.Editor.WriteMessage($"\nCircle {EntityId} has been created @{circle.Center}");
            }
        }
    }
 
    public class CadHelper
    {
        public static ObjectId CreateCircle(Document dwgPoint3d positiondouble radius)
        {
            var newId = ObjectId.Null;
            using (dwg.LockDocument())
            {
                using (var tran = dwg.TransactionManager.StartTransaction())
                {
                    var circle = new Circle();
                    circle.Center = position;
                    circle.Radius = radius;
                    circle.SetDatabaseDefaults();
 
                    var space = (BlockTableRecord)tran.GetObject(
                        dwg.Database.CurrentSpaceId, OpenMode.ForWrite);
                    newId = space.AppendEntity(circle);
                    tran.AddNewlyCreatedDBObject(circletrue);
 
                    tran.Commit();
                }
            }
 
            return newId;
        }
 
        public static bool TrySelectCenterPoint(out Point3d center)
        {
            center = Point3d.Origin;
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
            var res = dwg.Editor.GetPoint("\nSelect circle center:");
            if (res.Status == PromptStatus.OK)
            {
                center = res.Value;
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

4. The command class to run the UI
private static DrawForm _circleForm = null;
 
[CommandMethod("CreateCircle")]
public static void RunMyCommand()
{
    var dwg = CadApp.DocumentManager.MdiActiveDocument;
    var ed = dwg.Editor;
 
    if (_circleForm == null)
    {
        _circleForm = new DrawForm();
    }
    CadApp.ShowModelessDialog(CadApp.MainWindow.Handle, _circleForm);
}

Here is how AutoCAD behaves:

1. With AutoCAD 2026 or earlier (the code is based on .NET 8.0 or .NET Framework), there is no issue. Drag&Drop works as expected;

2. With the custom code built against either .NET 8.0 or .NET 10.0, loaded into AutoCAD 2027, the Drag&Drop operation would either do nothing or cause exception when Application.DoDragDrop() is called, depending on whether proper "try...catch..." or null test is applied.

In the aforementioned forum discussion thread, @ActivistInvestor pointed out the issue was caused by Microsoft's removal of BinaryFormatter from .NET 9, which is used in the Drag&Drop operation of Windows' desktop application. The removal of BinaryFormatter is out of security concerns (see this article).

While BinaryFormatter is removed from .NET 9/10, it can be added into custom development from NuGet package as separate reference, though, thus the suggested possible solution by @ActivistInvestor. However, I tried that and did not succeed. So, the code I showed here only works with AutoCAD 2026 and older, but not AutoCAD 2027. Since Autodesk is going to make AutoCAD 2025/2026 to support .NET 10 (i.e. the underline .NET runtime for AutoCAD 2025 or newer would required .NET 10.0), the Drag&Drop code currently works in AutoCAD2025/2026 would also stop once the .NET 10 upgrade service pack applied to AutoCAD 2025/2026. I suppose.

If some offices have custom AutoCAD plugins that use AutoCAD's Drag&Drop API, this is a bad news. Autodesk's support team has already known this issue being reported. I do not know whether/when fix/change to this broken API will be available. So, I kept exploring possible solution. Guess what, it turns out a rather simple fix/workaround is there to deal with this.

As the comments in the code I showed above (the overridden method OnDrop()), in AutoCAD 2027/.NET 10, the DragDropArgs.Data.GetData(Type t) method would return null if the data passed in is a object type. In Visual Studio's debugging mode, if examining the DragDropArgs' data, one would see that the data exists as "System_Com" object and its details are not available (because of the lack of BinaryFormatter in .NET 10). 

During the debugging, a bell suddenly rang in my head: why do I not try to pass a string value as IDataObject in the Application.DoDragDrop() is called? I went ahead and tried it. Voila, now the call to DragDropArgs.Data.GetData(typeof(string)) in OnDrop() method get the correct return - string value, not null.

So, the fix to this Drag&Drop issue becomes: serializing the data of a custom object into a string value and pass it into IDataObject when calling Application.DoDragDrop() and then deserializing the string value in IDataObject of the OnDrop() method back to the custom class object.

Here is my updated code (see the red lines):

using Autodesk.AutoCAD.Windows;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace DragAndDrop2027
{
    public class CircleInfo
    {
        public double Radius { getset; }
        public Point3d Center { getset; }
 
        public override string ToString()
        {
            return $"{Radius},{Center.X},{Center.Y},{Center.Z}";
        }
    }
 
    public static class CircleInfoExtension
    {
        extension(stringcircleInfoString)
        {
            public CircleInfoToCircleInfo()
            {
                if (circleInfoString != null)
                {
                    var data = circleInfoString.Split(';');
                    if (data.Length==4)
                    {
                        if (double.TryParse(data[0], out double radius))
                        {
                            var xOk = double.TryParse(data[1], out double x);
                            var yOk = double.TryParse(data[2], out double y);
                            var zOk = double.TryParse(data[3], out double z);
 
                            if (xOk && yOk && zOk)
                            {
                                Point3d ptnew Point3d(x,y,z);
                                return new CircleInfo() { Radius = radius, Center = pt };
                            }
                        }
                    }
                }
 
                return null;
            }
        }
    }
 
    public class CircleDropper
    {
        public static ObjectId DropCircleEntity(
            System.Windows.Forms.Form userUi,
            double radiusPoint3d center,
            Func<DocumentPoint3ddoubleObjectIdentityCreation)
        {
            var target = new CircleDropTarget(entityCreation);
 
            CircleInfo circle = new() { Radius = radius, Center = center };
            IDataObject dataObject = new DataObject();
            //dataObject.SetData(typeof(CircleInfo), circle);
            dataObject.SetData($"{circle.ToString()}");
 
            CadApp.DoDragDrop(userUidataObjectDragDropEffects.Copy, target);
 
            return target.EntityId;
        }
    }
 
    public class CircleDropTarget : DropTarget
    {
        private readonly Func<DocumentPoint3ddoubleObjectId> _createFunction;
 
        public CircleDropTarget(Func<DocumentPoint3ddoubleObjectIdentityCreation)
        {
            _createFunction = entityCreation;
        }
 
        public ObjectId EntityId { private setget; } = ObjectId.Null;
        public override void OnDrop(DragEventArgs e)
        {
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
 
            // Get data passed from Application.DoDragDrop()
            // With .NET 10.0, this GetData(Type) method would return null
            // if the Type argument is a object Type. This is where this
            // Drag&Drop operation fails
 
            // Remove this line
            //var data = e.Data.GetData(typeof(CircleInfo));
 
            // Add this line
            var data = e.Data.GetData(typeof(string));
 
            // The following code would do nothing because the data passed from
            // Application.DoDragDrop() is null, or raise exception if the "data" is
            // not tested for being null
            if (data != null)
            {
                // Remove this line
                //var circle = (CircleInfo)data;
 
                // Use the extension method to convert string value t CircleInfo object
                var circle = data.ToString().ToCircleInfo();
 
                //Create the entity
                EntityId = _createFunction(dwgcircle.Center, circle.Radius);
 
                CadApp.MainWindow.Focus();
                dwg.Editor.WriteMessage($"\nCircle {EntityId} has been created @{circle.Center}");
            }
        }
    }
 
    public class CadHelper
    {
        public static ObjectId CreateCircle(Document dwgPoint3d positiondouble radius)
        {
            var newId = ObjectId.Null;
            using (dwg.LockDocument())
            {
                using (var tran = dwg.TransactionManager.StartTransaction())
                {
                    var circle = new Circle();
                    circle.Center = position;
                    circle.Radius = radius;
                    circle.SetDatabaseDefaults();
 
                    var space = (BlockTableRecord)tran.GetObject(
                        dwg.Database.CurrentSpaceId, OpenMode.ForWrite);
                    newId = space.AppendEntity(circle);
                    tran.AddNewlyCreatedDBObject(circletrue);
 
                    tran.Commit();
                }
            }
 
            return newId;
        }
 
        public static bool TrySelectCenterPoint(out Point3d center)
        {
            center = Point3d.Origin;
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
            var res = dwg.Editor.GetPoint("\nSelect circle center:");
            if (res.Status == PromptStatus.OK)
            {
                center = res.Value;
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

In my code, for simplicity, I just added a ToString()/ToCircleInfo() method to convert the custom class object to/from string. For more complicated custom class, I might serialize the object class as json string, which is quite common code practice for most programmers these days.

Anyways, with this simple workaround, the Drag&Drop API code now works again, regardless AutoCAD versions/.NET versions.

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.