タブ(TabPage)毎に異なるコンテキストメニューを開く
ここには、タブをマウスの右ボタンで押し、そのボタンを離したときにタブに応じたコンテキストメニューを開くC#のソースコードを載せてある。このコンテキストメニューの開き方はVisual Studioでのそれに近い。
TabPageに直接コンテキストメニューを設定すると、上のタブ部分での右クリックに反応してくれない。
だからTabControlにコンテキストメニューを設定しないといけない。しかし、タブ毎に異なるコンテキストメニューを設定するメンバは無いし、クリックされたタブを簡単に取得できるメンバも無い。
ウィンドウメッセージなら、クリックされたタブが分かるらしいけど、ドトネで作ってるんだから、ドトネの中でやりたい。
そしたら以下のようなプログラムになった。短いし、要所要所でコメント入れてるのですぐ読めると思う。おおざっぱな流れは
- tabControl.MouseDownでクリックしたタブを取得
- tabControl.MouseUpでマウス右ボタンだったなら明示的にコンテキストメニューを開くよう指示
- contextMenuStrip.Openingで、タブがクリックされていたなら、タブにあった項目を用意してコンテキストメニューを開く
以下ソースコード
using System; using System.Windows.Forms; using System.ComponentModel; public class Form1 : Form { TabControl tabControl = new TabControl(); TabPage tabPage1 = new TabPage(); TabPage tabPage2 = new TabPage(); ContextMenuStrip contextMenuStrip = new ContextMenuStrip(); ToolStripMenuItem toolStripMenuItem1 = new ToolStripMenuItem(); ToolStripMenuItem toolStripMenuItem2 = new ToolStripMenuItem(); // クリックしたタブページ。タブがクリックされた瞬間だけタブページへの参照を入れる。それ以外の時はnull TabPage clickedTabPage = null; public Form1() { // コンテキストメニューの項目 toolStripMenuItem1.Text = "toolStripMenuItem1"; toolStripMenuItem2.Text = "toolStripMenuItem2"; toolStripMenuItem1.Click += delegate (object sender, EventArgs e) { MessageBox.Show(sender.ToString()); }; toolStripMenuItem2.Click += delegate (object sender, EventArgs e) { MessageBox.Show(sender.ToString()); }; // マウスクリックしたタブをthis.clickedTabPageに取得 tabControl.MouseDown += delegate (object sender, MouseEventArgs e) { this.clickedTabPage = null; for (int i = 0; i < tabControl.TabCount; i++) { if (tabControl.GetTabRect(i).Contains(e.X, e.Y)) { this.clickedTabPage = (TabPage)tabControl.GetControl(i); tabControl.SelectedTab = this.clickedTabPage; } } }; // マウスの右ボタンが離された時にコンテキストメニューを開くよう指示 tabControl.MouseUp += delegate (object sender, MouseEventArgs e) { if (this.clickedTabPage != null) { if (e.Button == MouseButtons.Right) { contextMenuStrip.Show((TabControl)sender, e.Location); } } }; // コンテキストメニューを開く直前にコンテキストメニューの項目を組み立てる contextMenuStrip.Opening += delegate (object sender, CancelEventArgs e) { ContextMenuStrip menu = (ContextMenuStrip)sender; if (this.clickedTabPage != null) { // タブがクリックされていたならコンテキストメニューを開く toolStripMenuItem1.Text = this.clickedTabPage.Text + " : " + "toolStripMenuItem1"; toolStripMenuItem2.Text = this.clickedTabPage.Text + " : " + "toolStripMenuItem2"; menu.Items.Clear(); menu.Items.Add(toolStripMenuItem1); menu.Items.Add(toolStripMenuItem2); this.clickedTabPage = null; } else { // タブがクリックされてないならコンテキストメニューを開かない e.Cancel = true; } }; // 最初に項目が追加しておかないと、初回だけコンテキストメニューが出ない。何で? contextMenuStrip.Items.Add(new ToolStripMenuItem()); // タブの表示文字列 tabPage1.Text = "tabPage1"; tabPage2.Text = "tabPage2"; // tabControlにタブとコンテキストメニューを設定 tabControl.TabPages.Add(tabPage1); tabControl.TabPages.Add(tabPage2); tabControl.ContextMenuStrip = contextMenuStrip; this.Controls.Add(tabControl); } public static void Main() { Application.Run(new Form1()); } }
フィールドclickedTabPageは、TabControlのサブクラスに含めた方が適切かな