skysan's programming notebook

コーディングして思ったことなどを気ままに

コンテキストメニューを閉じるときのクリックイベントを無効にする

目的

枠外をクリックして、コンテキストメニューを閉じたときに、クリックイベントが走らないようにします。

以下のサンプルでは、コンテキストメニューのクリックで赤に、PictureBoxのクリックで青に変更しています。
コンテキストメニューの[Red]コマンドを実行せずに、コンテキストメニューアイテムの外かつPictureBox上でクリックしたとき、PictureBoxのクリックイベントが実行され、青くなります。
f:id:skysan:20180201005738g:plain

方法

ウィンドウ内のどこかをクリックしたときに、IMessageFilterを使用してMouseUpイベントをキャンセルします。
イベントの発生順は

  1. contextMenuStrip1_Closed
  2. IMessageFilter.PreFilterMessage:左マウスダウン(WinMsg.LBUTTONDOWN)
  3. IMessageFilter.PreFilterMessage:左マウスアップ(WinMsg.LBUTTONUP)
  4. (pictureBox1_Click:PictureBox上でマウスアップした場合) ←無効にしたいイベント

です。
contextMenuStrip1_ClosedのToolStripDropDownClosedEventArgs.CloseReasonプロパティを使用して、コンテキストメニューを閉じた原因からマウスアップイベントを無効にするか判定します。

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{

    public partial class Form1 : Form
    {
        myClickFilter clickFilter;

        public Form1()
        {
            InitializeComponent();
            clickFilter = new myClickFilter();
            this.Load += Form1_Load;
            this.FormClosed += Form1_FormClosed;
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            Application.RemoveMessageFilter(clickFilter);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Application.AddMessageFilter(clickFilter);
        }

        /// <summary>
        /// コンテキストメニューが閉じたとき
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void contextMenuStrip1_Closed(object sender, ToolStripDropDownClosedEventArgs e)
        {
            Console.WriteLine("contextMenuStrip1_Closed:{0}", e.CloseReason);
            // アプリ上でどこかをクリックされたとき
            if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked)
            {
                clickFilter.RegistCancelMouseUp();
            }
        }

        /// <summary>
        /// コンテキストメニューのアイテムクリック時
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void toolStripMenuItem_Click(object sender, EventArgs e)
        {
            pictureBox1.BackColor = Color.Red;
        }

        /// <summary>
        /// PictureBoxのクリック時
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pictureBox1_Click(object sender, EventArgs e)
        {
            pictureBox1.BackColor = Color.Blue;
        }
    }

    public class myClickFilter : IMessageFilter
    {
        private enum WinMsg
        {
            LBUTTONDOWN = 0x0201,
            LBUTTONUP = 0x0202
        }
        private enum CancelStatus
        {
            None = 0,
            Set,
            Canceled,
        }
        private CancelStatus _status = CancelStatus.None;

        /// <summary>
        /// 左マウスアップイベントのキャンセルを設定
        /// </summary>
        public void RegistCancelMouseUp()
        {
            _status = CancelStatus.Set;
        }

        public bool PreFilterMessage(ref Message m)
        {
            if (m.Msg == (int)WinMsg.LBUTTONDOWN)
            {
                if (_status == CancelStatus.Canceled)
                {
                    _status = CancelStatus.None;
                }
            }
            else if (m.Msg == (int)WinMsg.LBUTTONUP)
            {
                if (_status == CancelStatus.Set)
                {
                    _status = CancelStatus.Canceled;
                    Console.WriteLine("Cancel LBUTTONUP");
                    //左マウスアップイベントをキャンセル
                    return true;
                }
            }
            // 通常
            return false;
        }
    }
}

結果

f:id:skysan:20180201005724g:plain