none
フォームにおけるmouseleaveイベント RRS feed

  • 質問

  • windows 7 64bitで visual studio 11を使っています。

    開発言語visual C#でWindowsアプリケーションを作っています。

    まだまだ勉強中ですがよろしくお願いいたします。

     

    フォームからマウスが外れたら閉じる設定にしたいと思っています。

    そのフォームにおいてmouselieveイベントで this.Close(); this.Dispose();を記述しても反応しません。

    そのフォーム内にはlistviewコントロールがひとつあるのですが、そのlistviewコントロールにおいて

    mouseleveイベントにthis.Close(); this.Dispose();を記述すると狙い通りフォームが閉じます。

    しかし、その場合listviewコントロールのスクロールバーに触れてもmouseleaveイベントが発生してしまうので

    どうしたらいいのかわからなくなりました。

     

    解決策よろしくお願いいたします。

    2012年1月22日 19:41

回答

  • MouseMoveで別のフォームがあるときは無効化して、ないときは領域外に出たら閉じるという命令をつくればいいのでしょうか?

    それで良いと思います。
    MouseMoveやMouseLeaveはクライアント領域内でのみ有効ですので、ウインドウ領域から外れた時に判断したいのでしたら、以下のようにWM_NCMOUSELEAVE を使えば良いでしょう。

    private const int WM_NCMOUSELEAVE = 0x02A2;
    
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
    
        switch (m.Msg)
        {
            case WM_NCMOUSELEAVE:
                if (!this.Bounds.Contains(Cursor.Position))
                    this.Close();
                break;
        }
    }
    

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク millionx 2012年1月23日 11:53
    2012年1月23日 6:53
    モデレータ
  • 一つ問題がありまして、マウスの動きが速いと反応してくれませんねぇ。

    確かにそうですね。これを回避するのは難しそうです。
    この代替案としては、millionxさんが上でご紹介されていたatmarkitの記事を参考にして、タイマーを使って一定間隔で監視した方が良さそうです。
    フォームのMouseEnterイベントでタイマーをスタートさせ、100ms間隔で監視させます。これだとマウスポイントを高速に動かしても原理上、問題なく動作します。

    private void Form1_MouseEnter(object sender, EventArgs e)
    {
        this.timer1.Start();
    }
    
    
    private void timer1_Tick(object sender, EventArgs e)
    {
        if (!this.Bounds.Contains(Cursor.Position))
            this.Close();
    
    }
    

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク millionx 2012年1月26日 12:37
    2012年1月25日 5:21
    モデレータ

すべての返信

  • 手元のVS2008でやってみましたが、イベント自体は動いているように思います。
    public Form1()
    {
      this.MouseLeave += new EventHandler(Form1_MouseLeave); // これがあるか確認
    }
    
    void Form1_MouseLeave(object sender, EventArgs e)
    {
        this.Close();
    }

    "// これがあるか確認" のイベントの登録がコンストラクタかForm1.Designer.csにちゃんとあるか確認してください。

    で、それはできたとして、たとえばフォームにDataGridViewなどがある場合、その上にマウスが行くと閉じてしまいますが、
    それは意図した動作ではないですよね?なのでMouseLeaveではだめなのではと思います。

    2012年1月23日 1:02
  • 素早い回答ありがとうございます。

    Designer.csのほうに登録されていましたのでそこはクリアされました。

     

    フォーム外にマウスが出ると閉じるフォームにはlistviewコントロールだけです。

    で、投稿した後もう一つ問題が発覚しました。そのlistviewコントロールでさらに別のフォームを出したいのですが、

    それを出すとMouseLeaveイベントが発生してしまうので、おっしゃるとおりMouseLeaveではだめなようです。

     

    MouseMoveで別のフォームがあるときは無効化して、ないときは領域外に出たら閉じるという命令をつくればいいのでしょうか?

    まだ試していませんが、下記リンクで実現できそうな気がしますので試してみたいと思います、

    http://www.atmarkit.co.jp/fdotnet/dotnettips/382ctrlcontain/ctrlcontain.html

    2012年1月23日 5:06
  • 例えばですが、MouseLeaveイベントの中で下記の動作をしてはいかがですか?

                bool doClose = false;
                if (Cursor.Position.X < 0)
                {
                    doClose = true;
                }
                if (Cursor.Position.Y < 0)
                {
                    doClose = true;
                }
                if (Cursor.Position.X > this.Width)
                {
                    doClose = true;
                }
                if (Cursor.Position.Y > this.Height)
                {
                    doClose = true;
                }
                if (doClose)
                {
                    this.Close();
                }
    

    2012年1月23日 5:23
  • MouseMoveで別のフォームがあるときは無効化して、ないときは領域外に出たら閉じるという命令をつくればいいのでしょうか?

    それで良いと思います。
    MouseMoveやMouseLeaveはクライアント領域内でのみ有効ですので、ウインドウ領域から外れた時に判断したいのでしたら、以下のようにWM_NCMOUSELEAVE を使えば良いでしょう。

    private const int WM_NCMOUSELEAVE = 0x02A2;
    
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
    
        switch (m.Msg)
        {
            case WM_NCMOUSELEAVE:
                if (!this.Bounds.Contains(Cursor.Position))
                    this.Close();
                break;
        }
    }
    

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク millionx 2012年1月23日 11:53
    2012年1月23日 6:53
    モデレータ
  • ありがとうございました。Formに上記を記述するだけで狙い通りの動作が得られました。
    2012年1月23日 11:54
  • 回答ありがとうございます。

    私の環境ではなぜかフォームのMouseLeaveイベントが起きないみたいです。

    しかし、ご提示いただいたコードは別のとこで大変参考になりました。

    実際動くならば以下のようにしたほうがいいでしょうね。

                bool doClose = false;
               if (Cursor.Position.X < this.Location.X)
                {
                    doClose = true;
                }
                if (Cursor.Position.Y < this.Location.Y)
                {
                    doClose = true;
                }
    
                if (Cursor.Position.X > this.Width)
                {
                    doClose = true;
                }
                if (Cursor.Position.Y > this.Height)
                {
                    doClose = true;
                }
                if (doClose)
                {
                    this.Close();
                }
    2012年1月24日 9:08
  • 一つ問題がありまして、マウスの動きが速いと反応してくれませんねぇ。

    まあ、許容範囲ではありますが。

     

    2012年1月24日 9:10
  • Position と Location の件、すいません未検証でした。

    確認しましたが、どちらにせよダメっぽいですね。

    Form_MouseLeaveだと、Formの内側(Border部の内側)から出る時にイベントが発生してしまい、

    恐らく求めるタイミングと違ってしまいそうです。

    ※FormBorderStyle = None のケースであれば求める動作をしそうですが、意味無いですよね。

     

    WndProc でOKとの事なので構わないのですが、気になったので・・・

     

    >>MouseLeaveイベントが起きないみたいです。

    ハンドラが消されてしまっているとか、何かの間違いじゃないですかね。

    2012年1月24日 9:52
  • イベントハンドラがあるのは確認したのですがなぜかダメです。

    ですが、新しいプロジェクトを作って下記を試してみると動きまして、今のプロジェクトで何故ダメなのか検討が付きませんがとりあえず現状維持で行きたいと思います。

    いずれにせよ、MouseLeaveだと新しいフォームが開けないので今回の場合は使えないということになりました。

            private void Form1_MouseLeave(object sender, EventArgs e)
            {
                this.Close();
                this.Dispose();
            }

    2012年1月24日 14:42
  • 一つ問題がありまして、マウスの動きが速いと反応してくれませんねぇ。

    確かにそうですね。これを回避するのは難しそうです。
    この代替案としては、millionxさんが上でご紹介されていたatmarkitの記事を参考にして、タイマーを使って一定間隔で監視した方が良さそうです。
    フォームのMouseEnterイベントでタイマーをスタートさせ、100ms間隔で監視させます。これだとマウスポイントを高速に動かしても原理上、問題なく動作します。

    private void Form1_MouseEnter(object sender, EventArgs e)
    {
        this.timer1.Start();
    }
    
    
    private void timer1_Tick(object sender, EventArgs e)
    {
        if (!this.Bounds.Contains(Cursor.Position))
            this.Close();
    
    }
    

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク millionx 2012年1月26日 12:37
    2012年1月25日 5:21
    モデレータ
  • 一つ問題がありまして、マウスの動きが速いと反応してくれませんねぇ。


    Windows 98/NT4 から、OS に通知機能が増え、TracMouseEvent API を呼び出すことで (NC) MOUSELEVE メッセージを漏れなく受信することができるようになっています。

    こちらの機能で対応できるかもしれません。

    2012年1月25日 7:09
  • 何度もありがとうございます。

    私の場合はこの方法が一番いいかもしれませんね。

    MouseEnterもやっぱり効かないので中のListViewで同様の記述をしたら希望通りの動きでした。


    • 編集済み millionx 2012年1月26日 12:37 誤字訂正
    2012年1月25日 13:30
  • 回答ありがとうございます

    APIの記述に関してはまだ全然知らないのでこれから勉強していきたいと思っています。参考にさせて頂きます。

    2012年1月25日 13:31