目的
タブコントロールのタブヘッダのテキストを直接編集したくなりました。
そう、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();
}
}
}
実行結果はこんな感じ…
ここで、問題(?)と云うか奇妙な特性に気が付いてしまいました。
フォームを表示しているので、入力している最中のアクティブウィンドウのスクショは、そこしか映らないという…
ま、そこはしょうがないと思って、諦める事にしました。
真面目に使うには、入力可能文字のチェックしたり、テキストの長さを制限したりしないといけないと思うけど、これを肉付けしていけば良いでしょう。
きっと。