2009-07-31 19 views
17

"ElementHost"の中に含まれているWPFコントロールで、アプリケーションのWinforms部分からデータをドラッグしようとしています。私がそうしようとするとクラッシュする。WinForms Interop、ドラッグアンドドロップからWPF

WinformsからWinformsまでは同じことを試しても問題ありません。 (下のサンプルコードを参照してください)

私はこの仕事をする上で助けが必要です...私が間違っていることを手がかりがありますか?

ありがとうございます!


例:以下のサンプルコードで
が、私はちょうど1)System.Windows.Forms.TextBox(Winformsの上のラベルコントロールにドラッグ複製を開始したときに作成したカスタムMyContainerClassオブジェクトをドラッグしようとしています)と2)System.Windows.TextBox(WPF、ElementHostに追加された)。

ケース1)は正常ですが、GetData()を使用してドロップデータを取得しようとするとケース2)がクラッシュします。 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; 
    } 
} 

答えて

16

@ペダリ& jmayor:提案ありがとうございました!

かなりの実験、試行錯誤、そして少し "Reflector'ing"の後、私は謎めいたエラーメッセージを受け取っていた理由を正確に把握することができました。 "Error HRESULT E_FAIL has been COMコンポーネントへの呼び出しから返されました "。

データWPF < - > Winformsを同じアプリケーションでドラッグすると、そのデータになります。

すべてのクラスを「シリアライズ可能」に変換するのがどれほど難しいかを確認しました。私はいくつかの理由で本当に苦痛を感じました。1つは、すべてのクラスを実質的にシリアライズ可能にする必要があり、2つのクラスにはコントロールへの参照があります。コントロールはシリアル化できません。したがって、メジャーのリファクタリングが必要でした。

任意のクラスのオブジェクトを同じアプリケーション内でWPFからドラッグするために、Serializable属性とISerializableを実装したラッパークラスを作成することにしました。私は実際のドラッグデータとなる "オブジェクト"型の1つのパラメータを持つ1つのコンストラクタを持っています。そのラッパーは、シリアル化/デシリアライズ時に、オブジェクト自体ではなく、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のfunctionnalityを保つために、私は次の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); 

ので、おかげで再び!皆さんは私に良いアイデアを教えてくれました。

-Oli

+0

おかげでこれを掲示するために、それは私が解決する助け私のコードでも同様の問題。私はIDataObject実装に[Serializable]を追加しました。突然、不思議なE_FAIL:が停止しました。 –

0

はたぶんイベントは反対の方法です。 PreviewDragEnterはWPFTextBoxと関連している必要があります。 DragEventArgsクラスもご覧ください。 System.Windows.Form(Windowsフォームバージョン)とSystem.Windows(WPFバージョン)のいずれかに存在します。

5

私は「似たような」問題を抱えていましたので、私が見つけたものを少なくとも教えてもらえます。

ドラッグアンドドロップ操作が最も簡単な場合に実行されると、.NETではOLEリモート処理が使用されているようです。何らかの理由でGetDataPresentが成功し、GetDataが失敗します。これはさらに、.NETフレームワークにIDataObjectのいくつかのバージョンが存在するという事実によって謎に包まれています。

Windowsフォームの既定値はSystem.Windows.Forms.IDataObjectです。しかし、あなたの場合は、代わりにSystem.Runtime.InteropServices.ComTypes.IDataObjectを与えることができます。私の議論であるhereをチェックすることもできます。

これが役に立ちます。

2

一見光栄です。私はそれを試しましたが、実装上にいくつかのエラーがありました。 私は何か誤りを訂正し始めました。ポインタを持っていない、もっと単純なものを探すことを決めました(私はそれを気に入っていません。特にカーバイドコレクションですが、実際の影響があるかどうかはわかりません。 )、それは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); 
     } 
    } 
} 
関連する問題