Drag n drop treeview Nodes between two different applications
-
I'm having some trouble implementing drag & drop between two treeviews in two different winform applications. It works just fine between two treeviews in the same application though. What I do is this:
private void OnDragDrop(object sender, System.Windows.Forms.DragEventArgs e) { ... if (e.Data.GetDataPresent(System.Type.GetType("MyNameSpace.MyTreeNode2"))) { data = (MyTreeNode2)e.Data.GetData(System.Type.GetType("MyNameSpace.MyTreeNode2")); if (data != null) MyTreeNode2 newFolder = (MyTreeNode2)data.Clone(); ... }
(The MyTreeNode2 class is inherited from the standard TreeNode class, adding only some extra properties) This will work fine when doing it with strings, probably since the format for strings is well understood or something while the format of my treenode object is not so easily shared between applications. Do I need to implement some kind of serialization stuff for my object for this to work, and pass the string? I know that there are a couple of predefined data formats available, but in java with WFC (which is where I've just this method before and it worked better I used the object type DataFormats.CF_WFCOBJECT and it worked fine. Is there a corresponding solution implemented in .NET/C#? (It works for a single child node in my treeview, but not for a folder with child elements...) Can anybody help me? -
I'm having some trouble implementing drag & drop between two treeviews in two different winform applications. It works just fine between two treeviews in the same application though. What I do is this:
private void OnDragDrop(object sender, System.Windows.Forms.DragEventArgs e) { ... if (e.Data.GetDataPresent(System.Type.GetType("MyNameSpace.MyTreeNode2"))) { data = (MyTreeNode2)e.Data.GetData(System.Type.GetType("MyNameSpace.MyTreeNode2")); if (data != null) MyTreeNode2 newFolder = (MyTreeNode2)data.Clone(); ... }
(The MyTreeNode2 class is inherited from the standard TreeNode class, adding only some extra properties) This will work fine when doing it with strings, probably since the format for strings is well understood or something while the format of my treenode object is not so easily shared between applications. Do I need to implement some kind of serialization stuff for my object for this to work, and pass the string? I know that there are a couple of predefined data formats available, but in java with WFC (which is where I've just this method before and it worked better I used the object type DataFormats.CF_WFCOBJECT and it worked fine. Is there a corresponding solution implemented in .NET/C#? (It works for a single child node in my treeview, but not for a folder with child elements...) Can anybody help me?Yes, you'll need to marshal the data for transport across application boundaries. The
Marshal
class may be of some help. Perhaps define a struct or something easy to marshal, fill that with information from theTreeNode
, marshal it as anIntPtr
or something (declaring it in global memory, for example), and then pack that in theDataObject
. That would probably be the easiest solution. The way you're doing it now would work within the instance of the application. Also, the format must be the same. So unless these two applications are sharing the assembly that defines yourMyNameSpace.MyTreeNode2
class, then you need to just specify a string (maybe something like "MyNameSpace.MyTreeNode2" instead ofType.GetType("MyNameSpace.MyTreeNode2")
, which - defined in different assemblies - would yield a different type string).Microsoft MVP, Visual C# My Articles
-
Yes, you'll need to marshal the data for transport across application boundaries. The
Marshal
class may be of some help. Perhaps define a struct or something easy to marshal, fill that with information from theTreeNode
, marshal it as anIntPtr
or something (declaring it in global memory, for example), and then pack that in theDataObject
. That would probably be the easiest solution. The way you're doing it now would work within the instance of the application. Also, the format must be the same. So unless these two applications are sharing the assembly that defines yourMyNameSpace.MyTreeNode2
class, then you need to just specify a string (maybe something like "MyNameSpace.MyTreeNode2" instead ofType.GetType("MyNameSpace.MyTreeNode2")
, which - defined in different assemblies - would yield a different type string).Microsoft MVP, Visual C# My Articles
Isn't it possible to serialize the raw object data from memory into a binary data format and just transport that? If I just define the MyTreeNode2 class as serializable with the [Serializable] attribute before the class definition and then I should be able to Serialize it into a byte array or something right? And that should be no problem to marshal I reckon... From what I could conclude there is built-in support for this type of binary serialization and deserialization, (BinaryFormatter class) which should mean that I should be able to use this method without a problem to transport an object.. So, if my thinking is right, can you tell me what's wrong with this? All I know is it doesn't do what I want it to... (code handling start drag event)
// Create a formatter object IFormatter formatter = new BinaryFormatter(); // Create a memoryStream MemoryStream ms = new MemoryStream(); // Serialize the object (here of type MyNameSpace.MyTreeNode2) into the memory stream using the binary formatter formatter.Serialize(ms, draggedItem); // Put the byte array from the memory stream into a data object and specify the format // type to be of type "MyNameSpace.MyTreeNode2" DataObject dObj = new DataObject( "MyNamespace.MyTreeNode2", ms.GetBuffer() ); selectedTreeView.DoDragDrop( dObj, DragDropEffects.All );
and here is what I want to use on the receiving side. But it doesn't work, the method actually returns after the last line (data = ...) without even continuing execution of the remaining part of the method, or at least the debug stepper of VS won't continue past this point. (Why does this happen?) (code for handling drop event)// check the data member of drag event argument for the data we're willing to receive if ( e.Data.GetDataPresent("MyNamespace.MyTreeNode2") ) { MyTreeNode2 data = null; IFormatter formatter = new BinaryFormatter(); byte[] serializedBytes = (byte[])e.Data.GetData("MyNamespace.MyTreeNode2", false); MemoryStream ms = new MemoryStream( serializedBytes ); data = (MyTreeNode2)formatter.Deserialize( ms ); ...
-
Isn't it possible to serialize the raw object data from memory into a binary data format and just transport that? If I just define the MyTreeNode2 class as serializable with the [Serializable] attribute before the class definition and then I should be able to Serialize it into a byte array or something right? And that should be no problem to marshal I reckon... From what I could conclude there is built-in support for this type of binary serialization and deserialization, (BinaryFormatter class) which should mean that I should be able to use this method without a problem to transport an object.. So, if my thinking is right, can you tell me what's wrong with this? All I know is it doesn't do what I want it to... (code handling start drag event)
// Create a formatter object IFormatter formatter = new BinaryFormatter(); // Create a memoryStream MemoryStream ms = new MemoryStream(); // Serialize the object (here of type MyNameSpace.MyTreeNode2) into the memory stream using the binary formatter formatter.Serialize(ms, draggedItem); // Put the byte array from the memory stream into a data object and specify the format // type to be of type "MyNameSpace.MyTreeNode2" DataObject dObj = new DataObject( "MyNamespace.MyTreeNode2", ms.GetBuffer() ); selectedTreeView.DoDragDrop( dObj, DragDropEffects.All );
and here is what I want to use on the receiving side. But it doesn't work, the method actually returns after the last line (data = ...) without even continuing execution of the remaining part of the method, or at least the debug stepper of VS won't continue past this point. (Why does this happen?) (code for handling drop event)// check the data member of drag event argument for the data we're willing to receive if ( e.Data.GetDataPresent("MyNamespace.MyTreeNode2") ) { MyTreeNode2 data = null; IFormatter formatter = new BinaryFormatter(); byte[] serializedBytes = (byte[])e.Data.GetData("MyNamespace.MyTreeNode2", false); MemoryStream ms = new MemoryStream( serializedBytes ); data = (MyTreeNode2)formatter.Deserialize( ms ); ...
An exception might be thrown that would cause execution to stop. What I described is a way of "serializing" it. You're persisting the object in global memory. Here you might have a problem depending on how .NET's
DataObject
handles a buffer. You still can't simply cross application boundaries with reference data (of which an array is). It needs to be either packed into memory (like strings and primitives) or locked in global memory (with COM, there's other ways where you can persist data to storage or stream it, but .NET doesn't expose that which is why I've been working on a library that - among other things - contains such an implementation). Also keep in mind that simply serializing yourTreeNode
derivative might not be appropriate. Remember that there are other public properties that theBinaryFormatter
will traverse and serialize, like theParent
property (so you're actually serializing almost an entire tree because of the references). You should implementISerializable
and serialize only what you need (Text
,Tag
, etc.). Don't forget about the constructor with the serialization signature.Microsoft MVP, Visual C# My Articles