ダイアログ起動時には、初期化ファイルから、位置"LoadPoint"と検索条件"Machcase"の値を取り出して、前回終了時の状態を復元することにします。また、検索置換ダイアログはひとつしか起動できないよう、フラグ"Exists"を使用して初回起動時に立てるようにします。
検索語句と置換語句は、起動中は値を保持することにします。前回検索(置換)した語句がリスト化されて表示されます。ここで使用している"StringCollection"は、以前つくったオリジナルのクラスです。
Public Class FindDialog
Inherits System.Windows.Forms.Form
#Region " Windows フォーム デザイナで生成されたコード "
... (略) ...
#End Region
' 変数
Private m_tabs As TabControl ' 対象のタブコントロール
Private m_edit As ExpandedTextControl ' 対象のテキストボックス
Private m_findwords As StringCollection ' 検索語句の履歴
Private m_replacewords As StringCollection ' 置換語句の履歴
Private Shared m_exists As Boolean = False ' 起動済みフラグ
Private Shared m_point As Point ' 起動時の位置
Private Shared m_matchcase As Boolean ' 大文字小文字を区別するか
' プロパティ : Exists(ダイアログが起動済みかどうか)
Public Shared Property Exists() As Boolean
Get
Return m_exists
End Get
Set(ByVal Value As Boolean)
m_exists = Value
End Set
End Property
' プロパティ : LoadPoint(ダイアログ読み込み時の位置)
Public Shared Property LoadPoint() As Point
Get
Return m_point
End Get
Set(ByVal Value As Point)
m_point = Value
End Set
End Property
' プロパティ : MatchCase(大文字と小文字を区別するか)
Public Shared Property MatchCase() As Boolean
Get
Return m_matchcase
End Get
Set(ByVal Value As Boolean)
m_matchcase = Value
End Set
End Property
' New コンストラクタ
' ... 引数 owner : 親フォーム
' ... 引数 loadpoint : 読み込み時の位置
' ... 引数 matchcase : 大文字と小文字を区別するかどうか
Public Sub New(ByRef owner As Form, _
ByRef loadpoint As Point, _
ByVal matchcase As Boolean)
MyBase.New()
InitializeComponent()
Me.Owner = owner
m_point = loadpoint
m_matchcase = matchcase
m_exists = True
End Sub
' Load : ロード
Private Sub FindDialog_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles MyBase.Load
Me.Location = LoadPoint
chkMatchcase.Checked = m_matchcase
UpdateButtonState()
End Sub
' Closing : アンロード
Private Sub FindDialog_Closing(ByVal sender As Object, _
ByVal e As System.ComponentModel.CancelEventArgs) Handles MyBase.Closing
m_point = Me.Location
m_matchcase = chkMatchcase.Checked
m_exists = False
m_edit.Focus()
End Sub
' UpdateButtonState : ボタンの状態の更新
Private Sub UpdateButtonState()
If cbFind.Text = "" Then
bnFind.Enabled = False
bnReplace.Enabled = False
bnReplaceAll.Enabled = False
Else
bnFind.Enabled = True
bnReplace.Enabled = Not cbReplace.Text.Equals("")
bnReplaceAll.Enabled = Not cbReplace.Text.Equals("")
End If
End Sub
' 検索語コンボボックス : テキスト変更時
Private Sub cbFind_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles cbFind.TextChanged
UpdateButtonState()
End Sub
' 置換語コンボボックス : テキスト変更時
Private Sub cbReplace_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs)
Handles cbReplace.TextChanged
UpdateButtonState()
End Sub
End Class
"UodateButtonState"は、検索語句や置換語句の入力状態に応じてボタンの使用可・不可を切り替えるための関数です。
検索・置換語句のリストを保守するためのコードです。一度使用した語句はそれぞれのコレクションクラスへ格納し、コンボボックスのリストへ反映させています。
' UpdateWordsList : 検索(置換)語句リストの更新
' ... 引数 cb : 対象のコンボボックス(検索コンボボックスか置換コンボボックスか)
' ... 引数 words : 対象のリスト(検索語句リストか置換語句リストか)
Private Sub UpdateWordsList(ByRef cb As ComboBox, ByRef words As StringCollection)
Dim strWord As String = cb.Text ' 追加する語句
Dim nIndex As Integer ' インデックス
Const nMax As Integer = 10 ' 最大項目数
If strWord = "" Then Exit Sub
' リストの項目数によって処理を分岐
If words.Count > 0 Then
' 追加する語句が既出の場合は削除
nIndex = words.IndexOf(strWord)
If nIndex >= 0 Then
cb.Items.RemoveAt(nIndex)
words.RemoveAt(nIndex)
End If
' 語句をリストの先頭に挿入
cb.Items.Insert(0, strWord)
words.Insert(strWord, 0)
Else
' 語句を追加
cb.Items.Add(strWord)
words.Insert(strWord)
End If
' 最大項目数を越えたら古いものを削除
If words.Count > nMax Then
cb.Items.RemoveAt(nMax)
words.RemoveAt(nMax)
End If
cb.SelectedIndex = 0
End Sub
ところで、検索・置換語句はダイアログ側で保持するのではなくメインフォーム側で保持するようにします。ダイアログ側で保持すると、ダイアログが閉じた時に当然データも破棄されてしまいます。ダイアログ側はメインフォーム側のデータを参照するだけです。こうするとアプリケーション起動中はリストの値を保持できるようになります。
メインフォーム側の変数の宣言
Private m_findwords As StringCollection ' 検索語句集
Private m_replacewords As StringCollection ' 置換語句集
Private m_finderdata As StringCollection ' 検索ダイアログの位置
メインフォーム側の検索・置換メニューの実装
' 編集メニュー : 検索
Private Sub mnFind_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles mnFind.Click
If Not IsNothing(TC1.SelectedTab) Then
' 検索・置換語句がないときは作成する
If IsNothing(m_findwords) Then
m_findwords = New StringCollection()
End If
If IsNothing(m_replacewords) Then
m_replacewords = New StringCollection()
End If
' ダイアログを開いて参照データを送る
Dim dlgFind As New FindDialog(Me, FindDialog.LoadPoint, FindDialog.MatchCase)
dlgFind.SetFindData(TC1, m_findwords, m_replacewords)
dlgFind.Show()
End If
End Sub
ダイアログ側のコード
' SetFindData : 検索データの設定
' ... 引数 tabctr : 対象のタブコントロール
' ... 引数 words1 : 検索語句のリスト
' ... 引数 words2 : 置換語句のリスト
Public Sub SetFindData(ByRef tabctr As TabControl, _
ByRef words1 As StringCollection, ByRef words2 As StringCollection)
m_tabs = tabctr
m_edit = DirectCast(m_tabs.SelectedTab, TextPage).EditBox
m_findwords = words1
m_replacewords = words2
Dim elem As String
If m_findwords.Count > 0 Then
For Each elem In m_findwords
' 検索コンボボックスにデータを追加
cbFind.Items.Add(elem)
Next
End If
If m_replacewords.Count > 0 Then
For Each elem In m_replacewords
' 置換コンボボックスにデータを追加
cbReplace.Items.Add(elem)
Next
End If
End Sub
検索・置換の対象となるのは、タブコントロールのアクティブなページです。選択した文字列があれば、その範囲内のみを検索・置換し、ないときはキャレットの位置から文書の最後までが範囲となります。
' [次を検索] ボタン
Private Sub bnFind_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles bnFind.Click
Try
Dim nStart As Integer = 1 ' 検索開始位置(初期値1=文書の先頭文字)
' 比較方法(バイナリモードかテキストモードか)
Dim nCompare As CompareMethod _
= CType(IIf(chkMatchcase.Checked, vbBinaryCompare, vbTextCompare), CompareMethod)
' 選択文字列がある場合は検索開始位置を再指定
m_edit = DirectCast(m_tabs.SelectedTab, TextPage).EditBox
If StrComp(m_edit.SelectedText, cbFind.Text, nCompare) = 0 Then
nStart = m_edit.SelectionStart + m_edit.SelectionLength
ElseIf m_edit.SelectionStart > 0 Then
nStart = m_edit.SelectionStart
End If
' 検索実行・・・見つかったらその位置へ移動
Dim nHit As Integer = InStr(nStart, m_edit.Text, cbFind.Text, nCompare)
If nHit > 0 Then
With m_edit
.Focus()
.Select(nHit - 1, cbFind.Text.Length)
.ScrollToCaret()
End With
Else
' 見つからなくなった時点で検索を終了させる
Dim msg As String = "検索が終了しました。"
MessageBox.Show(msg, "検索/置換", _
MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
' 検索語句リストの保守
UpdateWordsList(cbFind, m_findwords)
Catch ex As Exception
MessageBox.Show(ex.Message, "検索エラー", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
' [置換検索] ボタン
Private Sub bnReplace_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles bnReplace.Click
Try
' 検索語句と置換語句を入れ替える
m_edit = DirectCast(m_tabs.SelectedTab, TextPage).EditBox
If m_edit.SelectedText = cbFind.Text Then
m_edit.SelectedText = cbReplace.Text
m_edit.SelectionStart = m_edit.SelectionStart + cbReplace.Text.Length
End If
' 次の語句を検索
bnFind.PerformClick()
' 置換語句のリストの保守
UpdateWordsList(cbReplace, m_replacewords)
Catch ex As Exception
MessageBox.Show(ex.Message, "置換検索エラー", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
' [すべて置換] ボタン
Private Sub bnReplaceAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles bnReplaceAll.Click
Try
m_edit = DirectCast(m_tabs.SelectedTab, TextPage).EditBox
' 開始位置、長さ、終了位置の取得
Dim m_start As Integer = 1
Dim m_length As Integer = 0
Dim m_end As Integer = m_edit.TextLength
If m_edit.SelectionLength > 0 Then
' 選択文字列があるときは各位置を再設定
m_start = m_edit.SelectionStart
m_length = m_edit.SelectionLength
m_end = m_edit.SelectionStart + m_edit.SelectionLength
End If
' 置換の実行
Dim nStart As Integer = m_start ' 次の検索開始位置
Dim nHit As Integer = -1 ' 見つかった位置
Dim nCount As Integer = 0 ' 置換した数
Dim nBalance As Integer = _
cbReplace.Text.Length - cbFind.Text.Length ' 置換語句の長さー検索語句の長さ
Dim nCompare As CompareMethod _
= CType(IIf(chkMatchcase.Checked, vbBinaryCompare, vbTextCompare), CompareMethod)
Do
' ヒットした位置がなくなるまでループ
nHit = InStr(nStart, m_edit.Text, cbFind.Text, nCompare)
If nHit < 1 OrElse nHit > m_end Then
Exit Do
Else
m_edit.Select(nHit - 1, cbFind.Text.Length)
m_edit.SelectedText = cbReplace.Text
nStart = nHit + cbReplace.Text.Length ' 次の開始位置
m_end += nBalance ' 検索終了位置の修正
nCount += 1 ' 置換した数の合計
End If
Loop
' 置換後の処理
Dim msg As String = nCount & "個の置換が完了しました。"
MessageBox.Show(msg, "すべて置換", _
MessageBoxButtons.OK, MessageBoxIcon.Information)
If nCount > 0 Then
m_edit.Modified = True
If m_length > 0 Then m_length += (nBalance * nCount)
End If
m_edit.Select(m_start, m_length) ' 選択語句を復元
m_edit.Focus()
UpdateWordsList(cbFind, m_findwords) ' 検索語句リストの保守
UpdateWordsList(cbReplace, m_replacewords) ' 置換語句リストの保守
Catch ex As Exception
MessageBox.Show(ex.Message, "置換エラー", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
' [閉じる] ボタン
Private Sub bnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles bnClose.Click
Me.Close()
End Sub