2009-07-21 18 views
19

私はC#.NET 2.0 WinFormsアプリケーションを持っています。私のアプリケーションには、ラベルと何らかの編集コントロールの2つの子コントロールのコンテナがあります。私はマウスが親コントロールに入るか、離れたときに何かをしようとしていますが、マウスが移動する場合、私は気にしない親コントロールマウス子コントロールを使用したイベントの入力/終了

 
+---------------------------------+ 
| [Label Control] [Edit Control] | 
+---------------------------------+

:あなたは外箱が親コントロールであるところ、この、のように考えることができその子供の一人に私は、 "マウスは親または子の中のどこかにある"と "マウスが親のコントロール境界の外に移動しました"を表す単一のフラグを必要とします。

親コントロールと子コントロールの両方でMouseEnterとMouseLeaveを処理しようとしましたが、これはマウスがコントロールを移動するにつれてアクションが複数回開始され、終了することを意味します。言い換えれば、私はこれを取得:

 
Parent.OnMouseEnter  (start doing something) 
Parent.OnMouseLeave  (stop) 
Child.OnMouseEnter  (start doing something) 
Child.OnMouseLeave  (stop) 
Parent.OnMouseEnter  (start doing something) 
Parent.OnMouseLeave  (stop)

何でも私がやっているが開始してから停止しますように、中間OnMouseLeaveイベントは、いくつかの望ましくない影響を引き起こします。私はそれを避けたい。

子コントロールがマウスイベントを必要とし、メニューや他のショートカットキーを機能させたいので、親がマウスオーバーするとマウスをキャプチャしたくありません。

.NETフレームワーク内でこれを行う方法はありますか?または、Windowsマウスフックを使用する必要がありますか?

答えて

8

さらなる研究の末、Application.AddMessageFilter methodが見つかりました。これを使用して、私はマウスフックの.NETバージョンを作成しました:

class MouseMessageFilter : IMessageFilter, IDisposable 
{ 
    public MouseMessageFilter() 
    { 
    } 

    public void Dispose() 
    { 
     StopFiltering(); 
    } 

    #region IMessageFilter Members 

    public bool PreFilterMessage(ref Message m) 
    { 
     // Call the appropriate event 
     return false; 
    } 

    #endregion 

    #region Events 

    public class CancelMouseEventArgs : MouseEventArgs 
    {...} 

    public delegate void CancelMouseEventHandler(object source, CancelMouseEventArgs e); 
    public event CancelMouseEventHandler MouseMove; 
    public event CancelMouseEventHandler MouseDown; 
    public event CancelMouseEventHandler MouseUp; 

    public void StartFiltering() 
    { 
     StopFiltering(); 
     Application.AddMessageFilter(this); 
    } 

    public void StopFiltering() 
    { 
     Application.RemoveMessageFilter(this); 
    } 
} 

その後、私は、私のコンテナコントロールにMouseMoveイベントを処理し、マウスが私の親コントロール内にあるかどうかを確認し、作業を開始することができます。 (Iもそう、私は以前に開始した親を止めることができ、親コントロールにマウスを合わせ、最後を追跡する必要がありました。)

----編集----私のフォームクラスで

は、私が作成し、フィルタをフックアップ:

public class MyForm : Form 
{ 
    MouseMessageFilter msgFilter; 

    public MyForm() 
    {... 
     msgFilter = new MouseMessageFilter(); 
     msgFilter.MouseDown += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseDown); 
     msgFilter.MouseMove += new MouseMessageFilter.CancelMouseEventHandler(msgFilter_MouseMove); 
    } 

    private void msgFilter_MouseMove(object source, MouseMessageFilter.CancelMouseEventArgs e) 
    { 
     if (CheckSomething(e.Control) 
      e.Cancel = true; 
    } 
} 
2

これを解決するためにメッセージポンプをフックする必要はないと思います。あなたのUIのいくつかのフラグは、そのトリックを行う必要があります。私はあなたのOnMouseEnterハンドラの1つが呼び出されたときに、親コントロールの参照を取るコントロールクラスのControl _someParentのようなメンバー変数を作成すると考えています。次に、OnMouseLeaveで、_someParent "flag"の値をチェックし、現在の送信者のものと同じであれば、実際に処理を停止しないでください。親が異なる場合のみ、_someParentを停止してnullにリセットします。

+0

私は現在の親を残す場合は、私は知りません事前に私は子供のコントロールに行くか、実際に親を離れる。私があなたが示唆したようにしたら、親のOnMouseLeaveは "やあ、それは私だから処理を続ける"と言わなければならないだろう。 –

+0

あなたのコントロール格納構造体があまりにも柔軟である必要がない場合、つまりnレベルの包含である場合、および/または親の型を考慮に入れる場合は、この方法を使用することができます。 たとえば、Enterイベントが発生した場合、GroupBoxまたはPanel(ボタンを含むもの)が表示され、その値への参照を評価するまで、そのコントロールを取得して包含ツリーまで歩きます。それが変わるとき。 –

3

あなたはマウスがこのようなあなたのコントロールの境界内にあるかどうかを調べることができます(このコードを仮定すると、あなたのコンテナコントロールに存在し;、コンテナコントロールを参照してthisを交換しない場合):

private void MyControl_MouseLeave(object sender, EventArgs e) 
{ 
    if (this.ClientRectangle.Contains(this.PointToClient(Cursor.Position))) 
    { 
     // the mouse is inside the control bounds 
    } 
    else 
    { 
     // the mouse is outside the control bounds 
    } 
} 
+0

興味深い考えですが、実際にはうまくいかないようです。特定の親コントロールの最後のイベントセットは、Child.MouseEnterとChild.MouseLeaveです。私のChild.MouseLeaveハンドラでは、Cursor.Positionが親の境界にあることがあります。コードはカーソルがまだ内部にあると考えているので、 "マウスは親の中にある"アクションは決して停止しません。 –

+0

これは素晴らしい動作です!私はすべての子イベントを親クラスの同じハンドラに接続し、マウスの位置を確認しました。私は、マウスがコントロール内にあるかどうかを保存するブール値を保存しました。(親から子へのマウスの移動などの処理を控えるため) –

+0

これは、タスクマネージャのような一番上のウィンドウが部分的に上に画面の長方形のこのコードは、マウスがコントロールをカバーする一番上のウィンドウの一部に入ったときにマウスが残っていないかのように動作します。 – jnm2

5

私は、現在最も受け入れられている解決策よりはるかに優れた解決策を見つけたと感じました。

他の提案された解決策の問題は、それらがかなり複雑である(より低いレベルのメッセージを直接処理する)かのどちらかです。

または角の場合が失敗する:MouseLeaveでマウスの位置に頼ると、マウスが子コントロールの内側からコンテナの外側にまっすぐ進むと、マウスが抜けてしまうことがあります。このソリューションは、完全にエレガントではありませんが

、それは簡単で、動作します:

はあなたがMouseEnterイベントとMouseLeaveイベントを受信するコンテナの空間全体を占める透明コントロールを追加します。私はこれまで剥奪Making a control transparent

public class TranspCtrl : Control 
{ 
    public TranspCtrl() 
    { 
     SetStyle(ControlStyles.SupportsTransparentBackColor, true); 
     SetStyle(ControlStyles.Opaque, true); 
     this.BackColor = Color.Transparent; 
    } 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      CreateParams cp = base.CreateParams; 
      cp.ExStyle = cp.ExStyle | 0x20; 
      return cp; 
     } 
    } 
} 

使用例:

public class ChangeBackgroundOnMouseEnterAndLeave 
{ 
    public Panel Container; 
    public Label FirstLabel; 
    public Label SecondLabel; 

    public ChangeBackgroundOnMouseEnterAndLeave() 
    { 
     Container = new Panel(); 
     Container.Size = new Size(200, 60); 

     FirstLabel = new Label(); 
     FirstLabel.Text = "First Label"; 
     FirstLabel.Top = 5; 

     SecondLabel = new Label(); 
     SecondLabel.Text = "Second Lable"; 
     SecondLabel.Top = 30; 

     FirstLabel.Parent = Container; 
     SecondLabel.Parent = Container; 

     Container.BackColor = Color.Teal; 

     var transparentControl = new TranspCtrl(); 
     transparentControl.Size = Container.Size; 

     transparentControl.MouseEnter += MouseEntered; 
     transparentControl.MouseLeave += MouseLeft; 

     transparentControl.Parent = Container; 
     transparentControl.BringToFront(); 
    } 

    void MouseLeft(object sender, EventArgs e) 
    { 
     Container.BackColor = Color.Teal; 
    } 

    void MouseEntered(object sender, EventArgs e) 
    { 
     Container.BackColor = Color.Pink; 
    } 
} 

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

     var test = new ChangeBackgroundOnMouseEnterAndLeave(); 
     test.Container.Top = 20; 
     test.Container.Left = 20; 
     test.Container.Parent = this; 
    } 
} 

適切MouseLeaveとMouseEnterイベントをお楽しみください

は、私はここアメッドの答えで良い透明なコントロールを見つけましたイベント!

1

私は全く同じ必要性を持っていました。ポール・ウィリアムズの答えは私にコアアイデアを提供しましたが、コードを理解するのは難しかったです。私は別のものがhereを取るのを発見し、一緒に、2つの例は私自身のバージョンを開発するのを助けた。

初期化するには、目的のコンテナコントロールをContainerMessageFilterコンストラクタに渡します。このクラスは、コンテナのウィンドウハンドルとその中のすべての子コントロールを収集します。

操作中、クラスはWM_MOUSEMOVEメッセージをフィルタリングし、メッセージのHWndをチェックして、マウスがどのコントロール内を移動しているかを確認します。このようにして、マウスがコンテナ内のコントロールセット内またはコンテナ内をいつ見ているかを判断します。次の使用例で

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows.Forms; 

public class ContainerMessageFilter : IMessageFilter { 
    private const int WM_MOUSEMOVE = 0x0200; 

    public event EventHandler MouseEnter; 
    public event EventHandler MouseLeave; 

    private bool insideContainer; 
    private readonly IEnumerable<IntPtr> handles; 

    public ContainerMessageFilter(Control container) { 
     handles = CollectContainerHandles(container); 
    } 

    private static IEnumerable<IntPtr> CollectContainerHandles(Control container) { 
     var handles = new List<IntPtr> { container.Handle }; 

     RecurseControls(container.Controls, handles); 

     return handles; 
    } 

    private static void RecurseControls(IEnumerable controls, List<IntPtr> handles) { 
     foreach (Control control in controls) { 
      handles.Add(control.Handle); 

      RecurseControls(control.Controls, handles); 
     } 
    } 

    public bool PreFilterMessage(ref Message m) { 
     if (m.Msg == WM_MOUSEMOVE) { 
      if (handles.Contains(m.HWnd)) { 
       // Mouse is inside container 
       if (!insideContainer) { 
        // was out, now in 
        insideContainer = true; 
        OnMouseEnter(EventArgs.Empty); 
       } 
      } 
      else { 
       // Mouse is outside container 
       if (insideContainer) { 
        // was in, now out 
        insideContainer = false; 
        OnMouseLeave(EventArgs.Empty); 
       } 
      } 
     } 

     return false; 
    } 

    protected virtual void OnMouseEnter(EventArgs e) { 
     var handler = MouseEnter; 
     handler?.Invoke(this, e); 
    } 

    protected virtual void OnMouseLeave(EventArgs e) { 
     var handler = MouseLeave; 
     handler?.Invoke(this, e); 
    } 
} 

、我々はPanelのために、マウスの入口と出口を監視したいと子供はそれが含まれていることを制御します。

public partial class Form1 : Form { 
    private readonly ContainerMessageFilter containerMessageFilter; 

    public Form1() { 
     InitializeComponent(); 

     containerMessageFilter = new ContainerMessageFilter(panel1); 
     containerMessageFilter.MouseEnter += ContainerMessageFilter_MouseEnter; 
     containerMessageFilter.MouseLeave += ContainerMessageFilter_MouseLeave; 
     Application.AddMessageFilter(containerMessageFilter); 
    } 

    private static void ContainerMessageFilter_MouseLeave(object sender, EventArgs e) { 
     Console.WriteLine("Leave"); 
    } 

    private static void ContainerMessageFilter_MouseEnter(object sender, EventArgs e) { 
     Console.WriteLine("Enter"); 
    } 

    private void Form1_FormClosed(object sender, FormClosedEventArgs e) { 
     Application.RemoveMessageFilter(containerMessageFilter); 
    } 
} 
関連する問題