WinForms Interop,Drag&从WinForms中删除 - > WPF [英] WinForms Interop, Drag & Drop from WinForms -> WPF

查看:140
本文介绍了WinForms Interop,Drag&从WinForms中删除 - > WPF的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将数据从Winforms部分的应用程序拖到一个包含在ElementHost的WPF控件中。当我尝试这样做时,它会崩溃。



尝试相同的东西,但从Winforms到Winforms工作正常。 (见下面的示例代码)



我需要帮助来完成这项工作...有任何线索我做错了什么?



谢谢!






示例:

在下面的示例代码中, '只是试图拖动一个自定义的MyContainerClass对象,当在1)System.Windows.Forms.TextBox(Winforms)和2)System.Windows.TextBox(WPF,添加到ElementHost)时,在标签控件上启动拖动时创建的对象。



案例1)工作正常,但情况2)在尝试使用GetData()检索丢弃数据时崩溃。 GetDataPresent(WindowsFormsApplication1.MyContainerClass)返回true,所以在理论上,我应该能够在Winforms中检索这种类型的丢弃数据。



这是崩溃的堆栈跟踪:

 
错误HRESULT E_FAIL已经通过以下堆栈跟踪从调用COM组件返回:
在System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode,IntPtr errorInfo)
在System.Windows.Forms.DataObject .GetDataIntoOleStructs(FORMATETC&formatetc,STGMEDIUM&medium)
在System.Windows.Forms.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetDataHere(FORMATETC&formatetc,STGMEDIUM&medium)
在System.Windows.Forms .DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetData(FORMATETC&formatetc,STGMEDIUM&medium)
在System.Windows.DataObject.OleConverter.GetDataInner(FORMATETC&formatetc,STGMEDIUM&medium)
在System.Windows .DataObject.OleConverter.GetDataFromOleHGLOBAL(String format,DVASPECT aspect,Int32 index)
在System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(String forma t,DVASPECT方面,Int32索引)
在System.Windows.DataObject.OleConverter.GetData(String格式,Boolean autoConvert,DVASPECT方面,Int32索引)
在System.Windows.DataObject.OleConverter.GetData(字符串格式,Boolean autoConvert)
在System.Windows.DataObject.GetData(String format,Boolean autoConvert)
在System.Windows.DataObject.GetData(字符串格式)
在WindowsFormsApplication1.Form1。 WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs中的textBox_PreviewDragEnter(Object sender,DragEventArgs e)$ 48
$ / pre

这是一些代码: p>

  //  - 向您的表单添加ElementHost  -  
// - 在表单中添加标签 -

public partial class Form1:Form
{
public Form1()
{
InitializeComponent();

System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox();
textBox.Text =WPF TextBox;
textBox.AllowDrop = true;
elementHost2.Child = textBox;
textBox.PreviewDragEnter + = new System.Windows.DragEventHandler(textBox_PreviewDragEnter);

System.Windows.Forms.TextBox wfTextBox = new System.Windows.Forms.TextBox();
wfTextBox.Text =Winforms TextBox;
wfTextBox.AllowDrop = true;
wfTextBox.DragEnter + = new DragEventHandler(wfTextBox_DragEnter);
Controls.Add(wfTextBox);
}

void wfTextBox_DragEnter(object sender,DragEventArgs e)
{
bool dataPresent = e.Data.GetDataPresent(WindowsFormsApplication1.MyContainerClass);

// NO CRASH here!
object data = e.Data.GetData(WindowsFormsApplication1.MyContainerClass);
}

void textBox_PreviewDragEnter(object sender,System.Windows.DragEventArgs e)
{
bool dataPresent = e.Data.GetDataPresent(WindowsFormsApplication1.MyContainerClass) ;

// Crash appens here!
// {HRESULT E_FAIL已从对COM组件的调用返回。
对象数据= e.Data.GetData(WindowsFormsApplication1.MyContainerClass);
}

private void label1_MouseDown(object sender,MouseEventArgs e)
{
label1.DoDragDrop(new MyContainerClass(label1.Text),DragDropEffects.Copy);
}
}

public class MyContainerClass
{
public object Data {get;组;

public MyContainerClass(object data)
{
Data = data;
}
}


解决方案

Pedery& jmayor:谢谢你们的建议! (见下面的发现)



经过不少试验,试验和错误,还有一点反射器,我设法弄清楚为什么我是接收到隐含错误消息错误HRESULT E_FAIL已从对COM组件的调用返回。



这是因为当拖动数据WPF时 - > Winforms在同一个应用程序中,数据必须可序列化!



我已经检查了将我们所有的类到可序列化,我会有一个真正的痛苦有几个原因...一个,我们需要实际上使所有的类可序列化和两个,其中一些类有引用控件!控件不可序列化。因此,需要重构



所以...因为我们想通过任何对象任何类在同一个应用程序中拖动到WPF,我决定用Serializable属性创建一个包装类,并实现ISerializable。我将有1个参数,其中1个参数类型为object,这将是实际的拖动数据。这个包装器,当序列化/解串行化时,不会将对象本身序列化,而是将对象的IntPtr序列化(我们可以做的只是在我们的1个实例应用程序中只需要该功能)。请参见下面的代码示例:

  [Serializable] 
public class DataContainer:ISerializable
{
public object Data {get ;组;

public DataContainer(object data)
{
Data = data;
}

//反序列化构造函数
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成员

public void GetObjectData(SerializationInfo info,StreamingContext context)
{
GCHandle handle = GCHandle.Alloc (数据);
IntPtr address = GCHandle.ToIntPtr(handle);
info.AddValue(dataAddress,address);
}

#endregion
}

保持IDataObject功能,我创建了以下DataObject包装器:

  public class DataObject:IDataObject 
{
System.Collections.Hashtable _Data = new System.Collections.Hashtable();

public DataObject(){}

public DataObject(object data)
{
SetData(data);
}

public DataObject(字符串格式,对象数据)
{
SetData(format,data);
}

#region IDataObject成员

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 obj = new DataContainer(data);

if(string.IsNullOrEmpty(format))
{
//创建一个虚拟DataObject对象来检索所有可能的格式。
//例如:对于System.String类型,GetFormats返回3种格式:
//System.String,UnicodeText和Text
System.Windows.Forms。 DataObject dataObject = new System.Windows.Forms.DataObject(data);
foreach(dataObject.GetFormats()中的字符串fmt)
{
_Data [fmt] = obj;
}
}
else
{
_Data [format] = obj;
}
}

public void SetData(object data)
{
SetData(data,null);
}

#endregion
}

我们正在使用以下类:

  myControl.DoDragDrop(new MyNamespace.DataObject(myNonSerializableObject)); 

//在drop事件中,例如
e.Data.GetData(typeof(myNonSerializableClass));

我知道我知道...这不是很好的 但它正在做我们想要的。我们还创建了一个dragdrop帮助器类,它掩盖了DataObject的创建,并具有模板化的GetData函数来检索数据,而没有任何转换...有点像:

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

所以再次感谢回复!你们给了我很好的想法,看看可能的解决方案!



-Oli


I'm trying to drag data from the Winforms portion of my application on a WPF controls that's contained inside an "ElementHost". And it crashes when I try doing so.

Trying the same thing but from Winforms to Winforms works fine. (See example code below)

I need help on making this work... have any clues what I'm doing wrong?

Thanks!


Example:
In the sample code below, I'm just trying to drag a custom MyContainerClass object created when initating the drag on the label control on a 1) System.Windows.Forms.TextBox (Winforms) and 2) System.Windows.TextBox (WPF, added to an ElementHost).

Case 1) works fine but case 2) is crashing when trying to retrieve the drop data using GetData(). GetDataPresent("WindowsFormsApplication1.MyContainerClass") returns "true" so In theory, I should be able to retrive my drop data of that type like in Winforms.

Here is the stack trace of the crash:

"Error HRESULT E_FAIL has been returned from a call to a COM component" with the following stack trace:
 at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
 at System.Windows.Forms.DataObject.GetDataIntoOleStructs(FORMATETC& formatetc, STGMEDIUM& medium)
 at System.Windows.Forms.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetDataHere(FORMATETC& formatetc, STGMEDIUM& medium)
 at System.Windows.Forms.DataObject.System.Runtime.InteropServices.ComTypes.IDataObject.GetData(FORMATETC& formatetc, STGMEDIUM& medium)
 at System.Windows.DataObject.OleConverter.GetDataInner(FORMATETC& formatetc, STGMEDIUM& medium)
 at System.Windows.DataObject.OleConverter.GetDataFromOleHGLOBAL(String format, DVASPECT aspect, Int32 index)
 at System.Windows.DataObject.OleConverter.GetDataFromBoundOleDataObject(String format, DVASPECT aspect, Int32 index)
 at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert, DVASPECT aspect, Int32 index)
 at System.Windows.DataObject.OleConverter.GetData(String format, Boolean autoConvert)
 at System.Windows.DataObject.GetData(String format, Boolean autoConvert)
 at System.Windows.DataObject.GetData(String format)
 at WindowsFormsApplication1.Form1.textBox_PreviewDragEnter(Object sender, DragEventArgs e) in WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs:line 48  

Here is some code:

// -- Add an ElementHost to your form --
// -- Add a label to your form --

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        System.Windows.Controls.TextBox textBox = new System.Windows.Controls.TextBox();
        textBox.Text = "WPF TextBox";
        textBox.AllowDrop = true;
        elementHost2.Child = textBox;
        textBox.PreviewDragEnter += new System.Windows.DragEventHandler(textBox_PreviewDragEnter);

        System.Windows.Forms.TextBox wfTextBox = new System.Windows.Forms.TextBox();
        wfTextBox.Text = "Winforms TextBox";
        wfTextBox.AllowDrop = true;
        wfTextBox.DragEnter += new DragEventHandler(wfTextBox_DragEnter);
        Controls.Add(wfTextBox);
    }

    void wfTextBox_DragEnter(object sender, DragEventArgs e)
    {
        bool dataPresent = e.Data.GetDataPresent("WindowsFormsApplication1.MyContainerClass");

        // NO CRASH here!
        object data = e.Data.GetData("WindowsFormsApplication1.MyContainerClass");
    }

    void textBox_PreviewDragEnter(object sender, System.Windows.DragEventArgs e)
    {
        bool dataPresent = e.Data.GetDataPresent("WindowsFormsApplication1.MyContainerClass");

        // Crash appens here!!
        // {"Error HRESULT E_FAIL has been returned from a call to a COM component."}
        object data = e.Data.GetData("WindowsFormsApplication1.MyContainerClass");
    }

    private void label1_MouseDown(object sender, MouseEventArgs e)
    {
        label1.DoDragDrop(new MyContainerClass(label1.Text), DragDropEffects.Copy);
    }
}

public class MyContainerClass
{
    public object Data { get; set; }

    public MyContainerClass(object data)
    {
        Data = data;
    }
}

解决方案

@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

这篇关于WinForms Interop,Drag&amp;从WinForms中删除 - &gt; WPF的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆