A: 

Maybe the events are in the opposite way. The PreviewDragEnter should be related with the WPFTextBox. Also watch out the DragEventArgs class. There is one in System.Windows.Form ( Windows Form version) and the one under System.Windows( for WPF version).

jmayor
+2  A: 

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 not 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.

Pedery
+4  A: 

@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 constructor
protected 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 Members

public 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 Members

public 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 example
e.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

SuperOli