WinForms Interop, Drag & Drop from WinForms -> WPF WinForms Interop, Drag & Drop from WinForms -> WPF wpf wpf

WinForms Interop, Drag & Drop from WinForms -> WPF


@Pedery & jmayor: Thanks for the suggestions guys! (see my findings below)

After quite a few experimentation, trials and errors, and a bit of "Reflector'ing", I managed to figure out exactly why I was receiving the cryptic error message "Error HRESULT E_FAIL has been returned from a call to a COM component".

It was due to the fact that when dragging data WPF <-> Winforms in a same app, that data has to be Serializable!

I've checked how difficult it would be to transform all of our classes to "Serializable" and I would have a been a real pain for a couple of reasons... one, we would need to practically make all of classes serializable and two, some of these classes have references to Controls! And Controls aren't serializable. So a major refactoring would have been needed.

So... since we wanted to pass any object of any class to drag from/to WPF inside the same application, I decided to create a wrapper class, with the Serializable attribute and implementing ISerializable. I would have 1 contructor with 1 parameter of type "object" which would be the actual drag data. That wrapper, when serializing/de-serializing, would serialize not the object itself... but rather the IntPtr to the object (which we can do since we only want that functionnality inside our 1 instance only application.) See code sample below:

[Serializable]public class DataContainer : ISerializable{public object Data { get; set; }public DataContainer(object data){    Data = data;}// Deserialization constructorprotected DataContainer(SerializationInfo info, StreamingContext context){    IntPtr address = (IntPtr)info.GetValue("dataAddress", typeof(IntPtr));    GCHandle handle = GCHandle.FromIntPtr(address);    Data = handle.Target;    handle.Free();}#region ISerializable Memberspublic void GetObjectData(SerializationInfo info, StreamingContext context){    GCHandle handle = GCHandle.Alloc(Data);    IntPtr address = GCHandle.ToIntPtr(handle);    info.AddValue("dataAddress", address);}#endregion}

To keep the IDataObject functionnality, I created the following DataObject wrapper:

public class DataObject : IDataObject{System.Collections.Hashtable _Data = new System.Collections.Hashtable();public DataObject() { }public DataObject(object data){    SetData(data);}public DataObject(string format, object data){    SetData(format, data);}#region IDataObject Memberspublic object GetData(Type format){    return _Data[format.FullName];}public bool GetDataPresent(Type format){    return _Data.ContainsKey(format.FullName);}public string[] GetFormats(){    string[] strArray = new string[_Data.Keys.Count];    _Data.Keys.CopyTo(strArray, 0);    return strArray;}public string[] GetFormats(bool autoConvert){    return GetFormats();}private void SetData(object data, string format){    object obj = new DataContainer(data);    if (string.IsNullOrEmpty(format))    {        // Create a dummy DataObject object to retrieve all possible formats.        // Ex.: For a System.String type, GetFormats returns 3 formats:        // "System.String", "UnicodeText" and "Text"        System.Windows.Forms.DataObject dataObject = new System.Windows.Forms.DataObject(data);        foreach (string fmt in dataObject.GetFormats())        {            _Data[fmt] = obj;        }    }    else    {        _Data[format] = obj;    }}public void SetData(object data){    SetData(data, null);}#endregion}

And we are using the above classes like this:

myControl.DoDragDrop(new MyNamespace.DataObject(myNonSerializableObject));// in the drop event for examplee.Data.GetData(typeof(myNonSerializableClass));

I know I know... it's not very pretty... but it's doing what we wanted. We also created a dragdrop helper class which masks the DataObject creation and has templated GetData functions to retrieve the data without any cast... a bit like:

myNonSerializableClass newObj = DragDropHelper.GetData<myNonSerializableClass>(e.Data);

So thanks again for the replies! You guys gave me good ideas where to look at for possible solutions!

-Oli


I had a "similar" issue some time ago so I can at least tell you what I found out.

It seems .Net is resorting to OLE remoting when drag/drop operations are performed in but the simplest of cases. For some reason GetDataPresent will in these cases be successful and GetData will fail. This is furthermore mystified by the fact that there are several versions of the IDataObject in the .Net framework.

Windows Forms defaults to System.Windows.Forms.IDataObject. However, in your case you could try to give System.Runtime.InteropServices.ComTypes.IDataObject a shot instead. You can also check out my discussion here.

Hope this helps.


Seems wonderfull at first sight. I tried it but got some errors on implementations. I began to correct some errors when I decided to look for something a little bit more simplier, that do not have pointers (humm I don't like that, particularly with carbage collection, but I have no idea if it could have real impact) and that do not use Interop.

I come up with that. It works for me and I hope it will work for anybody else. It is only intended to be used for local drag drop (inside the same app).

How to use to drag:

DragDrop.DoDragDrop(listBoxOfAvailableScopes, new DragDropLocal(GetSelectedSimulResultScopes()),                                                DragDropEffects.Copy);

How to use to drop (get):

DragDropLocal dragDropLocal = (DragDropLocal)e.Data.GetData(typeof(DragDropLocal));            SimulResultScopes simulResultScopes = (SimulResultScopes)dragDropLocal.GetObject();

Code:

namespace Util{    [Serializable]    public class DragDropLocal    {        private static readonly Dictionary<Guid, object> _dictOfDragDropLocalKeyToDragDropSource = new Dictionary<Guid, object>();        private Guid _guid = Guid.NewGuid();        public DragDropLocal(object objToDrag)        {            _dictOfDragDropLocalKeyToDragDropSource.Add(_guid, objToDrag);        }        public object GetObject()        {            object obj;            _dictOfDragDropLocalKeyToDragDropSource.TryGetValue(_guid, out obj);            return obj;        }        ~DragDropLocal()        {            _dictOfDragDropLocalKeyToDragDropSource.Remove(_guid);        }    }}