テキスト入力をユーザーにさせるのに、だいたい入力する場所になんとなくのフォントイメージのまま入力させる処理を作るのに、ちょっと手間取りました。なんとなく同じように苦労する人が居そうだったので、記事化してメモします。
テキスト入力

  • フォーム設計
クリックした場所付近にで、なるべくイメージそのままに入力させたかったので、テキスト入力には RichTextBox を使用しました。そのコントロールを親にドッキングしただけのシンプルなフォームを DlgTextBox として用意しました。フォームのプロパティは以下のようにデフォルトから変更しました。
とにかくテキスト入力コントロール以外を見えなくするようにしています。
テキスト入力以外何も無い
ここで少し問題になったのは、RichTextBox にフォント情報をどう引き渡すかでした。コントロールを public にしても良かったのですが、今回は呼び出し側は親のウィンドウに対して設定を行い、テキスト入力ダイアログが Load されるタイミングで、ウィンドウの設定をコントロールに引き渡すようにしています。

//--------------------------------------------------------------- // フォームが読み込まれる //--------------------------------------------------------------- private void DlgTextBox_Load(object sender, EventArgs e) { Size = MainForm.Main.GetDrawSize(10, 2); RtbText.ForeColor = ForeColor; RtbText.BackColor = BackColor; RtbText.Font = Font; RtbText.LanguageOption = RichTextBoxLanguageOptions.UIFonts; }
 
RichTextBox にフォントを設定しただけでは、日本語は指定のフォントになりますが、半角英数文字が違うフォントになって大変おかしな表示になります。その問題を解決するのが LanguageOption プロパティです。本来は入力されるテキスト形式を指定の国に合わせるという指定ですが、これを RichTextBoxLanguageOptions.UIFonts とすることで、国に関係なく指定されたフォントだけで、テキストが表示されるようになります。
※ 日本人としてはUIFontsをデフォルトにして欲しかったです…
※ 高いけどそれなりのモノではあると思う。かなり欲しい…が、やっぱり高い💦

  • ダイアログのサイズ変更
サイズは横に10枠、縦に2枠としています。フォームのサイズ変更はフォーム生成時に呼び出し側から設定しても無視されます。そのため、フォームの読み込みタイミングで、自分で変更するようにしました。横に10枠は暫定です。最終的には入力された文字サイズに合わせて拡縮するように出来たら良いなと思っています。

縦に2枠というのは本来サイズの2倍です。これは、編集サイズが最小だった場合に、入力した文字が見えないという老眼な私には困った問題の対処です💦 まあ、だったら編集サイズを大きくしてから配置せよと言ってしまえばそれまでなんですが、2倍なら許容範囲だろうと思いました。
※ 後から調整するかもしれません。

あと、表示位置の調整にも少しだけ苦労しました。本マップエディタは、Mdi形式で組んでいます。編集エリアは Mdiの特定ウィンドウです。その位置は親のフォームのクライアント領域にドッキングしていますが、ツールバーと重なった位置にあったりします。そのため、ダイアログの表示位置は少しめんどくさい計算を行っています。ダイアログ生成を以下に示します。

var rcDraw = GetDrawRect(new(pos.X, pos.Y, 1, 1)); var font = RscText.GetFont(mcText.SelectID); using var dlg = new DlgTextBox() { StartPosition = FormStartPosition.Manual, Location = new( Location.X + EditScreen.Location.X + rcDraw.X, Location.Y + EditScreen.Location.Y + rcDraw.Y + ToolStrip.Height + rcDraw.Height), Font = new Font(font.Fntfamily, rcDraw.Height, font.Style), ForeColor = font.ForeColor, BackColor = font.FrameColor, };
まず大事なのは、StartPosition プロパティの設定です。FormStartPosition.Manual に設定しないと、勝手な位置に表示されてしまいます。そして、表示位置ですが「親フォームの位置+編集Mdi子ウィンドウの位置+編集座標」となります。高さにツールバーの高さを追加してます、また、実際の入力位置より1行分下げたかったので、枠の高さ分をさらに追加しています。


    • テキスト入力の完了と中断
    フォームに OK やキャンセルのボタンがないので、RichTextBox の入力情報を元にフォームを閉じるようにしました。具体的には [Enter] が押されたら正常終了、[Esc] が押されたら中断と見なします。この処理のために、RichTextBox の KeyPress イベントを使いました。以下がその処理です。

    //--------------------------------------------------------------- // キーが押された //--------------------------------------------------------------- private void RtbText_KeyPress(object sender, KeyPressEventArgs e) { switch ((Keys)e.KeyChar) { case Keys.Escape: DialogResult = DialogResult.Cancel; Close(); break; case Keys.Enter: DialogResult = DialogResult.OK; Close(); break; } }
    このダイアログは、別のアプリに操作が移っても、そのまま画面に対してトップレベルで残り続けるため、大変都合が悪いです。そのため、自分が非アクティブになったのを検知して、自分で自分を閉じる自爆型の実装としました。非アクティブになった時のイベントは Deactivate です。これは GUI には出ていないので、自前で設定する事になります。私はコンストラクタで実装しました。

    //--------------------------------------------------------------- // コンストラクタ //--------------------------------------------------------------- public DlgTextBox() { InitializeComponent(); Deactivate += new EventHandler(DlgTextBox_Deactivate); }
     
    Deactivate のイベント処理は以下の通りです。

    //--------------------------------------------------------------- // フォームが非アクティブになった //--------------------------------------------------------------- private void DlgTextBox_Deactivate(object sender, EventArgs e) { DialogResult = DialogResult.Cancel; Close(); }
    非アクティブになったという事は、テキスト中量を中断したと見なしています。まあ、自爆型と行っても、delete this; としているわけではないのですが💦 と、ここまでハマりポイントがあったので、似たような処理を作る際はお気をつけください。

    ※ フローリング面積が大きいのでうちだとこっちのほうが良いのかもしれない。すこしだけ安い…が、やっぱり高い💦💦💦