Introduction
Drag-and-drop can be very frustrating, particularly when one wants to do something simple. The .NET Framework documentation example on drag-and-drop runs to six printed pages. The documentation on the IDataObject
interface is even more frustrating. But I won't get started on that...
I've been banging my head all afternoon, when all I really wanted to do was figure out a pretty simple task: How to pass a custom object as a DragEventArgs
property? It should be pretty simple, and it turns out it is.
Passing a custom object
Here's the problem: Say I have declared a class, MyClass
, and instantiated an object, myObject
, from it. I want to pass myObject
as the Data
property of the DragEventArgs
parameter that accompanies all Drag-and-Drop events.
You might ask why I want to go to all this trouble. Wouldn't it be simpler to set a member variable to the object and use that? If I can do that, that's great. But if I am dragging from one UserControl to another UserControl, each of them is going to need additional plumbing to handle the operation. Things are much simpler if I can simply pass the drag object as the Data
property of DragEventArgs
.
It turns out that it's not at all difficult to do. The attached sample project shows how it's done. First, enable the AllowDrop
property in the drop target. Then, pass the drag object to DragEventArgs
in the DoDragDrop()
method of the control where the drag is initiated:
private void button1_MouseDown(object sender,
System.Windows.Forms.MouseEventArgs e)
{
MyClass myObject = new MyClass("TestObject");
button1.DoDragDrop(myObject, DragDropEffects.Copy);
}
In the sample program, we initiate the drag from a MouseDown
event. We instantiate the custom object, then pass it to DoDragDrop()
.
Next, when we enter the control that will be our drop target, we test the DragEventArgs
data to see if it is of a type the target can accept. We use the GetDataPresent()
method to accomplish that task:
private void textBox1_DragEnter(object sender,
System.Windows.Forms.DragEventArgs e)
{
bool dragObjectIsMyClass =
(e.Data.GetDataPresent(typeof(MyClass)) == true);
if (dragObjectIsMyClass);
{
e.Effect = DragDropEffects.Copy;
}
}
In this case, we're looking for an object of type MyClass
. The GetDataPresent()
method takes a System.Type
argument, so we get the type of our class using typeof()
. If we find the correct type in the e.Data
property, then we set the e.Effect
property to a value from the DragDropEffects
enum
. This is the mechanism that allows the control to finally accept the drop. If we don't set this property, the target control will show the slash-circle 'not available' cursor, and the control won't accept the drop.
With the target control primed and ready, we make the drop. The target fires its DragDrop
event, which we use to extract the MyClass
object we passed in from the drag control earlier:
private void textBox1_DragDrop(object sender,
System.Windows.Forms.DragEventArgs e)
{
MyClass retrievedObject = (MyClass)e.Data.GetData(typeof(MyClass));
textBox1.Text = retrievedObject.Name;
}
To extract the object, we call e.Data
's GetData()
method, passing it the type of our custom object. The object is returned as a generic object
, so we need to cast it as a MyClass
object. Once that's done, we have full access to the object. In the sample project, we simply place the object's name in the target control.
All in all, pretty straightforward, and a more elegant solution than passing objects around among member variables. If you have any questions, post them here. I'll be back in the next week or so to answer any that have been posted.
David Veeneman is a financial planner and software developer. He is the author of "The Fortune in Your Future" (McGraw-Hill 1998). His company, Foresight Systems, develops planning and financial software.