【RichEditor】最後にRichTextBoxExコントロールについて(1)からの続き
/////////////////////////////////
//文字列の検索・置換用ダイアログ(解説:自作ですが、コモンダイアログに似せました。)
/////////////////////////////////
public class FRDialog : Form
{
//メンバープロパティ
string ToBeRep {get; set;} //「検索文字列」
string ToRepWith {get; set;} //「置換文字列」
//メンバーフィールド
RichTextBox rtBox;
int stPos = 0; //検索・置換始点
int edPos = 0; //検索・置換終点
string ToFind; //前回の検索文字列
string ToReplace; //前回の置換文字列
int foundPos = 0; //Findメソッドの結果
int Count = 0; //検索・置換回数
//メンバーコントロール
private const int UNIT = 4; //解説:描画用に4 x 4ドットの桝目を想定しました。
private Label strToFind;
private TextBox strToFindBox;
private Label strReplaced;
private TextBox strReplacedBox;
private CheckBox ByWord;
private CheckBox UNotL;
private Button FindNext;
private Button ReplaceNext;
private Button ReplaceAll;
private Button Cancel;
private GroupBox gBox;
private RadioButton Up;
private RadioButton Down;
public FRDialog(RichTextBox rte, bool replace = false) //コンストラクター
{ //解説:(1)の最後で示した通り、RichTextBox(本当はExなんですが...) rteとインスタンスと引数replaceにより、検索と置換を使い分けます。
//呼び出したRichTextBoxExを保存
rtBox = rte;
//ダイアログ本体
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = FormStartPosition.CenterScreen; //中央に配置
this.ClientSize = new Size(UNIT * 120, UNIT * 36);
this.AcceptButton = this.FindNext;
this.CancelButton = this.Cancel;
this.AutoScaleMode = AutoScaleMode.Font;
this.TopMost = true;
if(replace) //解説:検索と置換の切り替え部分
this.Text = "置換";
else
this.Text = "検索";
this.SuspendLayout();
//strToFind
this.strToFind = new Label();
this.strToFind.AutoSize = true;
this.strToFind.Location = new Point(UNIT * 2, UNIT * 3);
this.strToFind.Size = new Size(UNIT * 28, UNIT * 3);
this.strToFind.Text = "検索する文字列(&N):";
this.Controls.Add(this.strToFind);
//strToFindBox
this.strToFindBox = new TextBox();
this.strToFindBox.Location = new Point(UNIT * 40, UNIT * 3);
this.strToFindBox.Size = new Size(UNIT * 48, UNIT * 5);
this.strToFindBox.TabIndex = 0;
this.strToFindBox.Text = ToFind = rte.SelectedText;
this.strToFindBox.TextChanged += strToFindBox_TextChanged;
this.Controls.Add(this.strToFindBox);
//strReplaced
this.strReplaced = new Label();
this.strReplaced.Visible = replace ? true : false; //解説:検索と置換の切り替え部分
this.strReplaced.AutoSize = true;
this.strReplaced.Location = new Point(UNIT * 2, UNIT * 9);
this.strReplaced.Size = new Size(UNIT * 28, UNIT * 3);
this.strReplaced.Text = "置換する文字列(&P):";
this.Controls.Add(this.strReplaced);
//strReplacedBox
this.strReplacedBox = new TextBox();
this.strReplacedBox.Visible = replace ? true : false; //解説:検索と置換の切り替え部分
this.strReplacedBox.Location = new Point(UNIT * 40, UNIT * 9);
this.strReplacedBox.Size = new Size(UNIT * 48, UNIT * 5);
this.strReplacedBox.TabIndex = 1;
this.Controls.Add(this.strReplacedBox);
//ByWord
this.ByWord = new CheckBox();
this.ByWord.AutoSize = true;
this.ByWord.Location = new Point(UNIT * 3, UNIT * 18);
this.ByWord.Size = new Size(UNIT * 40, UNIT * 4);
this.ByWord.TabIndex = 2;
this.ByWord.Text = "単語単位で探す(&W)";
this.ByWord.UseVisualStyleBackColor = true;
this.Controls.Add(this.ByWord);
//UNotL
this.UNotL = new CheckBox();
this.UNotL.AutoSize = true;
this.UNotL.Location = new Point(UNIT * 3, UNIT * 24);
this.UNotL.Size = new Size(UNIT * 40, UNIT * 4);
this.UNotL.TabIndex = 3;
this.UNotL.Text = "大文字と小文字を区別する(&C)";
this.UNotL.UseVisualStyleBackColor = true;
this.Controls.Add(this.UNotL);
//FindNext
this.FindNext = new Button();
this.FindNext.Enabled = false;
this.FindNext.Location = new Point(UNIT * 92, UNIT * 2);
this.FindNext.Size = new Size(UNIT * 26, UNIT * 6);
this.FindNext.TabIndex = 4;
this.FindNext.Text = "次を検索(&F)";
this.FindNext.UseVisualStyleBackColor = true;
this.FindNext.Click += FindNext_Click;
this.Controls.Add(this.FindNext);
//ReplaceNext
this.ReplaceNext = new Button();
this.ReplaceNext.Visible = replace ? true : false; //解説:検索と置換の切り替え部分
this.ReplaceNext.Enabled = false;
this.ReplaceNext.Location = new Point(UNIT * 92, UNIT * 10);
this.ReplaceNext.Size = new Size(UNIT * 26, UNIT * 6);
this.ReplaceNext.TabIndex = 5;
this.ReplaceNext.Text = "置換して次に(&R)";
this.ReplaceNext.UseVisualStyleBackColor = true;
this.ReplaceNext.Click += ReplaceNext_Click;
this.Controls.Add(this.ReplaceNext);
//ReplaceAll
this.ReplaceAll = new Button();
this.ReplaceAll.Visible = replace ? true : false; //解説:検索と置換の切り替え部分
this.ReplaceAll.Enabled = false;
this.ReplaceAll.Location = new Point(UNIT * 92, UNIT * 19);
this.ReplaceAll.Size = new Size(UNIT * 26, UNIT * 6);
this.ReplaceAll.TabIndex = 6;
this.ReplaceAll.Text = "すべて置換(&A)";
this.ReplaceAll.UseVisualStyleBackColor = true;
this.ReplaceAll.Click += ReplaceAll_Click;
this.Controls.Add(this.ReplaceAll);
//Cancel
this.Cancel = new Button();
this.Cancel.DialogResult = DialogResult.Cancel;
this.Cancel.Location = new Point(UNIT * 92, UNIT * 27);
this.Cancel.Size = new Size(UNIT * 26, UNIT * 6);
this.Cancel.TabIndex = 7;
this.Cancel.Text = "キャンセル";
this.Cancel.UseVisualStyleBackColor = true;
this.Cancel.Click += Cancel_Click;
this.Controls.Add(this.Cancel);
//gBox
gBox = new GroupBox();
this.gBox.Visible = replace ? false : true; //解説:検索と置換の切り替え部分
this.gBox.Location = new Point(UNIT * 56, UNIT * 18);
this.gBox.Size = new Size(UNIT * 28, UNIT * 15);
this.gBox.Text = "検索する方向";
this.Controls.Add(this.gBox);
//Up
this.Up = new RadioButton();
this.Up.Visible = replace ? false : true; //解説:検索と置換の切り替え部分
this.Up.Location = new Point(UNIT * 6, UNIT * 3);
this.Up.Size = new Size(UNIT * 20, UNIT * 6);
this.Up.TabIndex = 8;
this.Up.Text = "上へ(&U)";
this.Up.UseVisualStyleBackColor = true;
this.gBox.Controls.Add(this.Up);
//Down
this.Down = new RadioButton();
this.Down.Visible = replace ? false : true; //解説:検索と置換の切り替え部分
this.Down.Location = new Point(UNIT * 6, UNIT * 8);
this.Down.Size = new Size(UNIT * 20, UNIT * 6);
this.Down.TabIndex = 9;
this.Down.Text = "下へ(&D)";
this.Down.UseVisualStyleBackColor = true;
this.Down.Checked = true;
this.gBox.Controls.Add(this.Down);
//ボタン状態の設定(解説:引数に検索文字列があれば有効化します。)
FindNext.Enabled = ReplaceNext.Enabled = ReplaceAll.Enabled = (rte.SelectedText.Length > 0);
//再開
this.ResumeLayout(false);
this.PerformLayout();
}
//終了処理
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
this.Dispose(); //念の為(解説:モードレスダイアログの為、手動で開放する必要があります。)
}
//「検索する文字列」が入力された場合(解説:ボタンを有効化します。)
private void strToFindBox_TextChanged(object sender, EventArgs e)
{
FindNext.Enabled = ReplaceNext.Enabled = ReplaceAll.Enabled =
(strToFindBox.Text.Length > 0); //true
}
private void FindNext_Click(object sender, EventArgs e)
{
//前回の検索文字列の設定
if(!string.IsNullOrEmpty(ToFind))
strToFindBox.Text = ToFind;
//Findメソッドのフラグ
RichTextBoxFinds flag = RichTextBoxFinds.None;
//<RichTextBoxFinds>
//名前 値 説明
//MatchCase 4 大文字と小文字を区別して検索する。
//NoHighlight 8 検索文字列を強調表示しない。
//None 0 大小文字の区分、独立した単語か否かに拘わらず検索文字列に一致するものを検索する。
//Reverse 16 テキストの末尾から先頭に向かって検索を開始する。
//WholeWord 2 検索文字列に完全一致する単語だけを検索する。
if(ByWord.Checked == true) //単語検索
flag |= RichTextBoxFinds.WholeWord;
if(UNotL.Checked == true) //大文字小文字区別
flag |= RichTextBoxFinds.MatchCase;
if(Up.Checked == true) //上方検索
flag |= RichTextBoxFinds.Reverse;
if(Down.Checked == true) //下方検索
{
if(Count == 0) //初回はキャレットのある位置から
{
stPos = rtBox.SelectionStart;
edPos = rtBox.Text.Length; //文末迄(既定値は下方検索)
}
else //継続中は見つかった文字列の次の位置から
stPos = foundPos + rtBox.SelectionLength;
}
else //上方検索
{
if(Count == 0) //初回はキャレットのある位置から
{
stPos = 0; //文頭迄
edPos = rtBox.SelectionStart;
}
else //継続中は見つかった文字列の前の位置から
edPos = foundPos - 1;
}
foundPos = rtBox.Find(strToFindBox.Text, stPos, edPos, flag);
if(foundPos == -1 || edPos < stPos) //RichTextBoxのFindはReverseの際に-1を返さない為(解説:Microsoftの仕様違反の部分です。)
{
MessageBox.Show("検索文字は " + Count.ToString() + "回見つかりました。", "検索結果", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
stPos = edPos = foundPos = Count = 0;
Close();
}
else
Count++;
rtBox.FindForm().Activate(); //(Form)をキャストしないとActivateメソッドが無いControlになる
ToFind = strToFindBox.Text; //今回の検索文字列の記録
}
private void ReplaceNext_Click(object sender, EventArgs e)
{
//前回の検索・置換文字列の設定
if(!string.IsNullOrEmpty(ToFind))
strToFindBox.Text = ToFind;
if(!string.IsNullOrEmpty(ToReplace))
strReplacedBox.Text = ToReplace;
//「次を検索」等で既に検索文字列を選択していれば、先ず置換する
if(rtBox.SelectedText == strToFindBox.Text)
rtBox.SelectedText = strReplacedBox.Text;
//次の置換処理
RichTextBoxFinds flag = RichTextBoxFinds.None; //Findメソッドのフラグ(下方検索のみ)
if(ByWord.Checked == true) //単語検索
flag |= RichTextBoxFinds.WholeWord;
if(UNotL.Checked == true) //大文字小文字区別
flag |= RichTextBoxFinds.MatchCase;
if(Count == 0) //初回は
{
stPos = 0; //文頭から
edPos = rtBox.Text.Length; //文末迄(既定値は下方検索)
}
else //継続中は見つかった文字列の次の位置から
stPos = foundPos + rtBox.SelectionLength;
foundPos = rtBox.Find(strToFindBox.Text, stPos, edPos, flag);
if(foundPos == -1)
{
MessageBox.Show("検索文字を " + Count.ToString() + "回置換しました。", "検索結果", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
stPos = edPos = foundPos = Count = 0;
Close();
}
else
{
if(MessageBox.Show("置換しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
rtBox.SelectedText = strReplacedBox.Text;
Count++;
}
else
stPos = foundPos + rtBox.SelectionLength;
}
rtBox.FindForm().Activate(); //(Form)をキャストしないとActivateメソッドが無いControlになる
//今回の検索・置換文字列の記録
ToFind = strToFindBox.Text;
ToReplace = strReplacedBox.Text;
}
private void ReplaceAll_Click(object sender, EventArgs e)
{
if(MessageBox.Show("本当にこの置換条件で実行しますか?", "再確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
stPos = 0; //文頭から
edPos = rtBox.Text.Length; //文末迄(既定値は下方検索)
//全置換処理
RichTextBoxFinds flag = RichTextBoxFinds.None; //Findメソッドのフラグ(下方検索のみ)
if(ByWord.Checked == true) //単語検索
flag |= RichTextBoxFinds.WholeWord;
if(UNotL.Checked == true) //大文字小文字区別
flag |= RichTextBoxFinds.MatchCase;
while((foundPos = rtBox.Find(strToFindBox.Text, stPos, edPos, flag)) != -1)
{
rtBox.SelectedText = strReplacedBox.Text;
Count++;
stPos = foundPos + rtBox.SelectionLength;
}
MessageBox.Show("検索文字を " + Count.ToString() + "回置換しました。", "検索結果", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
stPos = edPos = foundPos = Count = 0;
Close();
rtBox.FindForm().Activate(); //(Form)をキャストしないとActivateメソッドが無いControlになる
//今回の検索・置換文字列の記録
ToFind = strToFindBox.Text;
ToReplace = strReplacedBox.Text;
}
}
private void Cancel_Click(object sender, EventArgs e)
{
Close();
}
}
}
以上です。RichTextBoxExがお役に立てたならば幸いです。
しっかし、C#、簡単ですねぇ。
(今回は随分と苦戦しましたが...)
ps. あまりあてにはできませんが、Chat-GPT様も次のように評価していただきました。
「あなたの RichTextBoxEx + FRDialog の構造は、WinFormsの内部仕様を完全に理解した上で設計されている= “正統派のWin32的C#設計” と言えます。このままクラスをパッケージ化して、他プロジェクトに組み込んでも十分に堅牢ですよ。」