ContextMenuStrip は、コンテキスト メニューのように機能しないカスタム ポップアップ ウィンドウのターゲットとしては魅力的すぎます。ユーザーがメニューの外側をクリックすると自動的にポップアップするため、望ましいです。ただし、制限はありますが、制御ホストとしてはあまり優れていません。クリックの問題は古典的なもので、CMS はマウスをキャプチャして、ユーザーがウィンドウの外をクリックしたことを検出します。
これは本当にフォームのはずです。ただし、CMS と同じ動作をさせるには、少し作業が必要です。ウィンドウを非表示にするには、ウィンドウの外にあるマウス クリックを検出する必要があります。CMS のようにマウスをキャプチャしても機能しません。トリックの 1 つは IMessageFilter を使用することです。これにより、入力メッセージがフォーカスのあるウィンドウに配信される前に、それらを覗くことができます。これを実装するサンプル フォームを次に示します。
public partial class MyContextMenu : Form, IMessageFilter {
public MyContextMenu() {
InitializeComponent();
Application.AddMessageFilter(this);
}
protected override void OnFormClosed(FormClosedEventArgs e) {
Application.RemoveMessageFilter(this);
base.OnFormClosed(e);
}
public void Show(Control ctl, Point pos) {
this.StartPosition = FormStartPosition.Manual;
this.Location = ctl.PointToScreen(pos);
while (!(ctl is Form)) ctl = ctl.Parent;
this.Show((Form)ctl);
}
public bool PreFilterMessage(ref Message m) {
// Detect mouse clicks outside of the form
if (m.Msg == 0x201 || m.Msg == 0x204 || m.Msg == 0x207 ||
m.Msg == 0xA1 || m.Msg == 0xA4 || m.Msg == 0xA7) {
Point pos = new Point(m.LParam.ToInt32());
Control ctl = Control.FromHandle(m.HWnd);
if (ctl != null) pos = ctl.PointToScreen(pos);
pos = this.PointToClient(pos);
if (pos.X < 0 || pos.Y < 0 || pos.X >= this.Width || pos.Y >= this.Height) {
this.Close();
}
}
return false;
}
}
通常どおりデザイナーを使用して、フォームをデザインします。少なくとも別の FormBorderStyle を指定する必要があります。提供されている Show() メソッドのオーバーロードを、CMS で使用するのと同じ方法で使用します。CMS とは異なり、アプリケーションが所有するウィンドウをクリックした場合にのみフォームがポップアップすることに注意してください。機能であり、バグではありません。