といっても、ただメニューを表示するだけなら簡単!!
public partial class CustomButton : Button
{
// ボタンクリックで表示するメニュー
public ContextMenuStrip DropDownMenu { get; set; }
// コンストラクタ
public CustomButton()
{
InitializeComponent();
}
protected override void OnClick(EventArgs e)
{
// メニューを表示可能か?
if (DropDownMenu != null)
{
// メニューを開く
DropDownMenu.Show(this, new Point(0, Height));
else
{
// クリックイベントを発生させる
base.OnClick(e);
}
}
}
で、これだけでとりあえずDropDownMenuプロパティに入れたメニューがボタンクリックで開くようになる。こんな感じ。
まあ、これだけでも概ねOKだけどメニューが開いている状態でもう一度ボタンをクリックすると、メニューが一瞬閉じてからすぐにまた開いてしまう。
メニューを開いている状態でボタンクリックしたら閉じて欲しいんだけど。。
ボタンクリックの前にメニューが閉じてしまうから「メニューが閉じられていたら開く」ってこともできない。
protected override void OnClick(EventArgs e)
{
// メニューを表示可能か?
if (DropDownMenu != null && !DropDownMenu.Visible) // ←すでにメニューは閉じているのでダメ!
{
// メニューを開く
DropDownMenu.Show(this, new Point(0, Height));
}
else
{
// クリックイベントを発生させる
base.OnClick(e);
}
}
「WM_MOUSEACTIVATE = 0x0021(マウスクリックによりアクティブウインドウが移った)」
つまり、メニューが閉じられてアクティブなウィンドウがメニューからフォームに移ったって意味らしい。(ちなみにメニューが開く時はフォームからメニューにアクティブウィンドウが移ったって意味になる)
しかもこのメッセージ、メニューが閉じる前に飛んできてるやん!!
というわけで、こんな感じに実装してみた。
public partial class CustomButton : Button
{
// WndProcのWM_MOUSEACTIVATE(マウスクリックによりアクティブウインドウが移った)のメッセージID
private const int WM_MOUSEACTIVATE = 0x0021;
// メニューのClosedイベント
private ToolStripDropDownClosedEventHandler closedHandler;
// Click時にメニューを表示可能であることを示すフラグ
private bool allowShowMenu = true;
// メニューがボタンクリックによって閉じられることを示すフラグ
private bool menuCloseByClick = false;
// ボタンクリックで表示するメニュー
public ContextMenuStrip DropDownMenu { get; set; }
// コンストラクタ
public CustomButton()
{
InitializeComponent();
// メニューのClosedイベントハンドラ生成
closedHandler = new ToolStripDropDownClosedEventHandler(MenuClosed);
}
protected override void OnClick(EventArgs e)
{
// メニューを表示可能か?
if (DropDownMenu != null && allowShowMenu)
{
// イベントハンドラ登録
DropDownMenu.Closed += closedHandler;
// メニューを開く
DropDownMenu.Show(this, new Point(0, Height));
}
else
{
// クリックイベントを発生させる
base.OnClick(e);
}
// 次回クリックに備えてメニュー表示可能フラグを立てる
allowShowMenu = true;
// 次回クリックに備えてメニューがボタンクリックによって閉じられたことを示すフラグをたたむ
menuCloseByClick = false;
}
// メニューが閉じられた時の処理
private void MenuClosed(object sender, ToolStripDropDownClosedEventArgs e)
{
// イベントハンドラを破棄
DropDownMenu.Closed -= closedHandler;
if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked && menuCloseByClick)
{
// ボタンクリックによってメニューが閉じられた場合
// この後に走るOnClickでメニューを開かないようにフラグを立てる
allowShowMenu = false;
}
else
{
// ボタンクリック以外でメニューが閉じられた場合
// 次回のボタンクリックでメニューが開くようにフラグをたたむ
allowShowMenu = true;
}
// ボタンクリック以外でメニューが閉じられた場合に備えて
// ここでボタンクリックによってメニューが閉じられたことを示すフラグをたたむ
menuCloseByClick = false;
}
// Windowメッセージを処理します
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
// ボタンクリックによってメニューが閉じられてアクティブウィンドウが移った場合フラグを立てる
// (正確にはメニューが表示される時にもフラグが立つがMenuClosedは走らないので問題ない)
menuCloseByClick = true;
}
base.WndProc(ref m);
}
}
やっていることは簡単!
1.WndProcでmenuCloseByClickフラグを立てる
2.メニューClosedイベントでmenuCloseByClickが立っていたらallowShowMenuフラグを立てる
3.ボタンクリックでallowShowMenuが立っていたらメニューを開いて、フラグを戻しておく
特に問題なさそうだし、これでOKかな。。。
あれ?ボタンを右クリックしてメニューを閉じたらどうなるんだろう???
・・・ボタンを2回クリックしないとメニューが開かなくなった。。○| ̄|_
ボタンをダブルクリックしてメニューを開くとメニューClosedイベントが2回走るし。。。
なんか、もう面倒臭くなってきたなぁ( ̄◇ ̄)
public partial class CustomButton : Button
{
// WndProcのWM_MOUSEACTIVATE(マウスクリックによりアクティブウインドウが移った)のメッセージID
private const int WM_MOUSEACTIVATE = 0x0021;
// メニューのClosedイベント
private ToolStripDropDownClosedEventHandler closedHandler;
// Click時にメニューを表示可能であることを示すフラグ
private bool allowShowMenu = true;
// メニューがボタンクリックによって閉じられることを示すフラグ
private bool menuCloseByClick = false;
// ボタンクリックで表示するメニュー
public ContextMenuStrip DropDownMenu { get; set; }
// コンストラクタ
public CustomButton()
{
InitializeComponent();
// メニューのClosedイベントハンドラ生成
closedHandler = new ToolStripDropDownClosedEventHandler(MenuClosed);
}
// マウス押下時の処理
protected override void OnMouseDown(MouseEventArgs mevent)
{
base.OnMouseDown(mevent);
// ボタン上で左クリック以外の場合、次回の左クリックで
// メニューが開くようにフラグを立てる
if (mevent.Button != MouseButtons.Left)
{
allowShowMenu = true;
}
}
// ボタンクリック時の処理
protected override void OnClick(EventArgs e)
{
// メニューを表示可能か?
if (DropDownMenu != null && allowShowMenu)
{
if (!DropDownMenu.Visible) // メニューが既に開いている場合は何もしない(ダブルクリック対策)
{
// イベントハンドラ登録
DropDownMenu.Closed += closedHandler;
// メニューを開く
DropDownMenu.Show(this, new Point(0, Height));
}
}
else
{
// クリックイベントを発生させる
base.OnClick(e);
}
// 次回クリックに備えてメニュー表示可能フラグを立てる
allowShowMenu = true;
// 次回クリックに備えてメニューがボタンクリックによって閉じられたことを示すフラグをたたむ
menuCloseByClick = false;
}
// メニューが閉じられた時の処理
private void MenuClosed(object sender, ToolStripDropDownClosedEventArgs e)
{
// イベントハンドラを破棄
DropDownMenu.Closed -= closedHandler;
if (e.CloseReason == ToolStripDropDownCloseReason.AppClicked && menuCloseByClick)
{
// ボタンクリックによってメニューが閉じられた場合
// この後に走るOnClickでメニューを開かないようにフラグを立てる
allowShowMenu = false;
}
else
{
// ボタンクリック以外でメニューが閉じられた場合
// 次回のボタンクリックでメニューが開くようにフラグをたたむ
allowShowMenu = true;
}
// ボタンクリック以外でメニューが閉じられた場合に備えて
// ここでボタンクリックによってメニューが閉じられたことを示すフラグをたたむ
menuCloseByClick = false;
}
// Windowメッセージを処理します
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
// ボタンクリックによってメニューが閉じられてアクティブウィンドウが移った場合フラグを立てる
// (正確にはメニューが表示される時にもフラグが立つがMenuClosedは走らないので問題ない)
menuCloseByClick = true;
}
base.WndProc(ref m);
}
// ボタン描画時の処理
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
// 三角形を描画
using (var brush = new SolidBrush(ForeColor))
{
float width = 7;
float height = 5;
float x = pevent.ClipRectangle.Width - width - 5;
float y = (pevent.ClipRectangle.Height - height) / 2;
var triangle = new PointF[] { new PointF(x, y)
, new PointF(x + width, y)
, new PointF(x + (width / 2), y + height) };
pevent.Graphics.FillPolygon(brush, triangle);
}
}
}
一応、ボタンの右端に▼も描画してみたw
とりあえず今日はこんなところで終わり。
0 件のコメント:
コメントを投稿