簡體   English   中英

WinForms互操作,從WinForms-> WPF拖放

[英]WinForms Interop, Drag & Drop from WinForms -> WPF

我試圖從“ ElementHost”中包含的WPF控件上的應用程序的Winforms部分中拖動數據。 當我嘗試這樣做時,它會崩潰。

嘗試相同的方法,但是從Winforms到Winforms都可以正常工作。 (請參見下面的示例代碼)

我需要進行這項工作的幫助...有任何線索我做錯了嗎?

謝謝!


例:
在下面的示例代碼中,我只是想拖動在1)System.Windows.Forms.TextBox(Winforms)和2)System.Windows.TextBox(WPF)上啟動標簽控件上的拖動時創建的自定義MyContainerClass對象。 ,添加到ElementHost中)。

情況1)工作正常,但情況2)在嘗試使用GetData()檢索放置數據時崩潰。 GetDataPresent(“ WindowsFormsApplication1.MyContainerClass”)返回“ true”,因此從理論上講,我應該能夠像Winforms一樣檢索該類型的放置數據。

這是崩潰的堆棧跟蹤:

"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

這是一些代碼:

// -- 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:謝謝你們的建議! (請參閱下面的發現)

經過大量的試驗,反復試驗和一些“反射”后,我設法弄清楚為什么我收到了一條隱式錯誤消息“從對COM組件的調用返回了錯誤HRESULT E_FAIL”。

這是由於在同一應用程序中拖動數據WPF <-> Winforms時,該數據必須可序列化!

我已經檢查了將所有類轉換為“可序列化”的難易程度,由於以下兩個原因,我會感到非常痛苦……一個,我們實際上需要使所有類都可序列化,而另外兩個,其中一些類引用了控件! 而且控件不可序列化。 因此,將需要進行重大的重構。

所以...由於我們希望傳遞任何類的任何對象以在同一應用程序內從WPF拖放到WPF,因此我決定創建一個具有Seri​​alizable屬性的包裝類並實現ISerializable。 我將有1個構造函數,其中1個參數為“ object”類型,這將是實際的拖動數據。 該包裝器在進行序列化/反序列化時,不會序列化對象本身……而是序列化到對象的IntPtr(我們可以這樣做,因為我們只希望在1個實例的應用程序中具有該功能。)請參見下面的代碼示例:

[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
}

為了保持IDataObject的功能,我創建了以下DataObject包裝器:

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
}

我們正在使用上述類:

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

// in the drop event for example
e.Data.GetData(typeof(myNonSerializableClass));

我知道我知道...這不是很漂亮 ...但是它正在做我們想要的。 我們還創建了一個拖放類幫助器類,該類掩蓋了DataObject的創建,並具有模板化的GetData函數來檢索數據而無需進行任何強制轉換。

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

因此,再次感謝您的答復! 你們給了我好主意,在哪里尋找可能的解決方案!

-奧利

前段時間我遇到了一個“類似”問題,所以我至少可以告訴您我發現了什么。

在最簡單的情況下,執行拖放操作時,.Net似乎正在使用OLE遠程處理。 由於某些原因,在這種情況下,GetDataPresent將成功,而GetData將失敗。 .Net框架中有多個版本的IDataObject,這使我感到困惑。

Windows窗體默認為System.Windows.Forms.IDataObject。 但是,在您的情況下,您可以嘗試給System.Runtime.InteropServices.ComTypes.IDataObject做個嘗試。 您也可以在這里查看我的討論。

希望這可以幫助。

一見鍾情。 我嘗試了一下,但是在實現上出現了一些錯誤。 當我決定尋找更簡單的東西時,我開始糾正一些錯誤,這些錯誤沒有指針(哼,我不喜歡這樣,特別是對於汽化器收集,但是我不知道它是否會產生真正的影響)並且不使用Interop。

我想到了。 它對我有用,我希望對其他人也有用。 它僅用於本地拖放(在同一應用程序內部)。

如何使用拖動:

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

如何使用拖放(獲取):

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

碼:

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);
        }
    }
}

也許事件是相反的。 PreviewDragEnter應該與WPFTextBox相關。 還要注意DragEventArgs類。 在System.Windows.Form(Windows窗體版本)中有一個,在System.Windows(WPF版本)下有一個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM