2016-12-27 13 views
3

ドラッグイベントを特定の方法で処理する必要があるwinformsコントロールがあります。このコントロールには、多くの子コントロールが含まれています。そのうちのいくつかは、ElementHostオブジェクトに埋め込まれたWPFです。これは、ドラッグイベントを処理するために必要な最上位のwinformsコントロールです。ホストされているWPFコントロールで飲み込まれたイベントを、そのwinformsの親にドラッグします。

マウスがwinformsコンポーネント内で親の上にある間に、簡単にドラッグ動作を実行できます。私がする必要のあることは何もありません。しかし、WPFコントロールはブラックホールです。ドラッグイベントを呑み込んで、メインのwinformsコントロールが正しく処理できないように見えます。

私は次のようにこの問題を回避ハックすることができます

  1. は私がトップを見つけるまで
  2. オーバーライドOnDragEnter WPFコントロール
  3. を再帰的にElementHostから始まるControl.Parent参照を歩くWPFコントロールにAllowDrop = trueを設定しましたレベルコントロールが必要です
  4. IDropTarget.OnDragEnter最上位のコントロールで見つかった

ご想像のとおり、これはかなり醜いです:

public MyWPFControl() 
{ 
    InitializeComponent(); 
    _host = new Lazy<System.Windows.Forms.Control>(BuildHostControl); 
    AllowDrop = true; 
} 

private System.Windows.Forms.Control BuildHostControl() 
{ 
    return new System.Windows.Forms.Integration.ElementHost 
    { 
     Dock = System.Windows.Forms.DockStyle.Fill, 
     Child = this, 
    }; 
} 

protected override void OnDragEnter(DragEventArgs e) 
{ 
    base.OnDragEnter(e); 
    if (e.Data.GetDataPresent(typeof(MyDragObject))) 
    { 
     var args = ConvertDragArgs(e); 
     var control = GetControl(_host.Value) 
     ((System.Windows.Forms.IDropTarget)control).OnDragEnter(args); 
    } 
} 

private System.Windows.Forms.DragEventArgs ConvertDragArgs(DragEventArgs e) 
{ 
    var dragObject = e.Data.GetData(typeof(MyDragObject)) as MyDragObject; 
    var position = e.GetPosition(this); 
    var data = new System.Windows.Forms.DataObject(dragObject); 
    return new System.Windows.Forms.DragEventArgs(data, (int)e.KeyStates, (int)position.X, (int)position.Y, System.Windows.Forms.DragDropEffects.Copy, System.Windows.Forms.DragDropEffects.Copy); 
} 

private MyTopLevelControl GetControl(System.Windows.Forms.Control control) 
{ 
    var cell = control as MyTopLevelControl; 
    if (cell != null) 
     return cell; 

    return GetControl(control.Parent); 
} 

同様の努力がOnDragDropのために取らなければならないが、簡潔にするために、私は確かに

それを省略している、私は挿入できトップレベルコントロールとWPFコントロールの両方の参照を持つヘルパーオブジェクトを使用して、ウォーキングコントロールを削除していくことができます。

しかし、私はそれを避けることができれば、ドラッグイベントをWPFコントロールで処理したいとは思っていません。トップレベルのコントロールでは、ドラッグしているときのように、子フォームのコントロールをwinformsします。これを達成する方法はありますか?

答えて

2

あなたの記述は完全に正常です。速いスピードで:AllowDropプロパティをTrueに設定すると、WinformsとWPFの両方でRegisterDragDrop() COM関数が呼び出されます。 IDropTargetインターフェイスのメソッドを認識し、同等の.NETイベントに直接マッピングします。この動作が始まるHWND引数を書き留めます。

Winformsでは、すべてのコントロールには独自のHandleプロパティがあります。とても特別なことは何も起こりません。イベントは、ホバリング/ドロップされているコントロールで発生します。これはWPFでは動作しません。コントロールにはハンドルがないため、外部コンテナのハンドルを借用します。通常は、WindowオブジェクトのHWndSourceです。そのイベントは、ルーティングされたイベントが行うものである適切なコントロールに向けられなければなりません。主な違いは、イベントが内側のコントロールから「外側のコントロール」に「バブル」するように見えることです。 WinformsまたはCOM配管のいずれにもそのようなバブリングはありません。したがって、埋め込まれたWPFコントロールにドラッグの使用がない場合、バックが停止する場所で、ユーザーはドラッグしないマウスカーソルを取得します。任意のWinformsアプリケーションで起こるように。

Winformsのプログラマは、通常、UIに明白なドラッグターゲットを与えることでこれを処理します。 "ここに落ちる"グリフに似ている何か。これは正確にユーザビリティを傷つけるわけではありませんが、ドラッグ&ドロップの機能はかなりわかりにくい傾向があります。それがあなたの望むものでなければ、あなたが持っている唯一の選択肢は、イベントを自分でバブルさせることです。あなたと同じように

あなたが必要とするもののほとんどは既にそこにあります。あなたはこれを最適な方法で実行しませんでした。最も明白なアプローチは、ElementHostでこれらのバブリングを行うことです。これらのコントロールのすべてに共通する機能です。これは簡単ですが、Winformsの問題の95%は、.NETクラスから独自のクラスを派生させることで解決されます。

using System; 
using System.Windows.Forms; 
using System.Windows.Forms.Integration; 

class ElementHostEx : ElementHost { 
    public ElementHostEx() { 
     this.ChildChanged += ElementHostEx_ChildChanged; 
    } 

    private void ElementHostEx_ChildChanged(object sender, ChildChangedEventArgs e) { 
     var prev = e.PreviousChild as System.Windows.UIElement; 
     if (prev != null) { 
      prev.DragEnter -= Child_DragEnter; 
      prev.Drop -= Child_Drop; 

     } 
     if (this.Child != null) { 
      this.Child.DragEnter += Child_DragEnter; 
      this.Child.Drop += Child_Drop; 
     } 
    } 
    // etc... 
} 

私はChild_Xxxxイベントハンドラを投稿しませんでしたが、あなたはすでにそのコードを持っています。新しいコントロールをツールボックスの上部からコンパイルしてドロップし、既存のElementHostを置き換えます。または、コードでElementHostの代わりにElementHostExを作成します。

関連する問題