目的
タブコントロールのタブヘッダのテキストを直接編集したくなりました。
そう、Excelのシート名変更のような操作感を実現したい訳ですね。
これです、この「tabPage1」とか云うのを、まさにこの場所で編集したいんです。
あーそうそう、以前やったListViewのカラム直接変更にちょっと似ているんですが、実現の仕方にはちょっと工夫が必要でした。
直接編集の仕組み
ListViewのカラムには直接編集の機能がありません。
なので、編集したいカラムの上にTextBoxをぴったり重ねて表示して、操作者を謀ろうと云うのがざっくりした仕組みの説明です。
あれ?
簡単に終わってしまった…
じゃ、タブの編集も同じ方針で
タブの直接編集機能も無い様なので、タブの上にTextBox重ねて表示すれば良いよね。
一番問題になりそうなのは、編集したいタブの位置をどうやって取得するのかなぁー
ま、ちょっと調べればちょちょい、で出来るんじゃないの?
と思いました、最初は。
ま、方向性は間違ってないんですけどね。
結果、ダメでした…
いや、単純な水平展開、って訳には行きませんでした、ってのが正解かな。
問題はどこに?
一応、おさらいしておくと、コンテナであるTabControlの配下に複数のTabPageが入ります。
其々のTabPageがタブ(ヘッダ)を持っている様なイメージがありますが(私だけ?)、実際の所TabPageはただの四角です。複数のTabPageの切換えのセレクタであるタブを持っているのはコンテナの方(TabControl)だったんですね。
なのでタブの上に編集用コントロールを乗っけるには、対象となるTextBoxをTabControlの子コントロールとして追加する必要があります。
んが、コンテナであるTabControlは狭量で、自分の子供はTabPageだけ、と決めつけている中々の頑固者です。
これでは、タブの上での直接編集が実現できません…
何か策はないのー
一筋の光明
TabControl家への養子入りはかなり無理そう…
ParentにあたるFormの配下ではTabControlの前面に出る事は出来ませんし、ChildになるTabPageの下では、その範囲は親のTabControlの領域までは手が届きません。
塞がったなー…
となれば、もう親類縁者に頼る事は諦めて、赤の他人としてTabControlの前に立ちはだかる方法を探っ…
あぁぁ、そうか。
もういっその事新しいFormを作っちゃうってのでどうでしょう?
中身はTextBox一個、FormBorderStyle.Noneにしてサイズはタブの耳と同じ大きさにする…
みたいな感じで。
実際の所、どんな見た目になるのかや、動きにぎこちなさが残らないのか―
微妙に心配のタネはあるものの、思いついたが百年目、じゃねーや
思い立ったが吉日と云う事で、取り敢えず手を動かしてみましょう。
こんな感じになりました
いきなり、サブクラス化してみた結果です。
コンポーネントを追加して拡張したので、Designer.csのthis.AutoScaleMode = ~~をコメントアウトする必要があります。
namespace TestTabControl {
partial class TabControlEx {
/// <summary>
/// 必要なデザイナー変数です。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 使用中のリソースをすべてクリーンアップします。
/// </summary>
/// <param name="disposing">マネージ リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
protected override void Dispose(bool disposing) {
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region コンポーネント デザイナーで生成されたコード
/// <summary>
/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
/// コード エディターで変更しないでください。
/// </summary>
private void InitializeComponent() {
components = new System.ComponentModel.Container();
// this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
}
#endregion
}
}
肝心のコードの方です。
ごめん…
コメントも何も一切無かったけど、長いコードじゃないので雰囲気で掴んでください。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestTabControl {
public partial class TabControlEx : TabControl {
public TabControlEx() {
InitializeComponent();
}
public void EditTabText() {
placeHolder = new Form();
placeHolder.SuspendLayout();
textbox = new TextBox();
textbox.BackColor = SystemColors.InactiveCaption;
textbox.BorderStyle = BorderStyle.None;
textbox.TextAlign = HorizontalAlignment.Center;
textbox.KeyPress += Textbox_KeyPress;
placeHolder.AutoScaleMode = AutoScaleMode.Font;
placeHolder.AutoSizeMode = AutoSizeMode.GrowAndShrink;
placeHolder.Controls.Add(textbox);
placeHolder.FormBorderStyle = FormBorderStyle.None;
placeHolder.TopMost = true;
placeHolder.Load += PlaceHolder_Load;
placeHolder.Deactivate += PlaceHolder_Deactivate;
placeHolder.ResumeLayout(false);
placeHolder.PerformLayout();
placeHolder.Show();
}
private void PlaceHolder_Load(object sender, EventArgs e) {
Rectangle tabRect = this.GetTabRect(this.SelectedIndex);
tabRect.Location = this.FindForm().PointToScreen(tabRect.Location);
placeHolder.DesktopBounds = tabRect;
textbox.Bounds = new Rectangle(0, (tabRect.Height - textbox.Height) / 2, tabRect.Width, tabRect.Height);
textbox.Text = this.SelectedTab.Text;
}
private void PlaceHolder_Deactivate(object sender, EventArgs e) {
if(!string.IsNullOrEmpty(textbox.Text))
this.SelectedTab.Text = textbox.Text;
placeHolder.Close();
}
private void Textbox_KeyPress(object sender, KeyPressEventArgs e) {
switch((Keys)e.KeyChar) {
case Keys.Escape:
textbox.Text = string.Empty;
goto case Keys.Enter;
case Keys.Enter:
e.Handled = true;
placeHolder.Hide();
return;
}
}
Form placeHolder = null;
TextBox textbox = null;
}
}
使い方は、FormにTabControlExを貼り付けてからの、以下のコード。
基本は以下の通り、MouseDoubleClickイベント捕まえて、EditTabText()を呼ぶだけ。
現在アクティブなタブの上に、小さなFormが乗っかってテキスト入力できるようになります。
namespace TestTabControl {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
private void tabControlEx1_MouseDoubleClick(object sender, MouseEventArgs e) {
tabControlEx1.EditTabText();
}
}
}
実行結果はこんな感じ…
ここで、問題(?)と云うか奇妙な特性に気が付いてしまいました。
フォームを表示しているので、入力している最中のアクティブウィンドウのスクショは、そこしか映らないという…
ま、そこはしょうがないと思って、諦める事にしました。
真面目に使うには、入力可能文字のチェックしたり、テキストの長さを制限したりしないといけないと思うけど、これを肉付けしていけば良いでしょう。
きっと。

