ものえおさむ

C#で作る、あなただけのメモ帳 ~検索機能の実装~

2009-03-06 16:56:00

はじめに
このブログでは、"Windows アプリケーションってどう作るのかよくわからない" といったプログラマ向けに Visual C# 2008 Express Edition (無償) を使用した Windows アプリケーションに作り方について説明しています。
Visual C# 2008 Express Edition (無償)(※) の入手方法については
この記事 を、プロジェクトのコンパイル方法については この記事を参照してください。

(※)大学生、専門学校生の方は DreamSpark で、商用の Visual Studio を無償で入手することもできます。

ずいぶん御無沙汰してしまいたがお元気でしょうか?

tech・Days Japan 2009 後、HOSTING-PRO 2009(※) の準備その他があり、ばたばたしているうちに風邪をひいて寝込み、こんなに時間が経ってしまいました。
(※本職では Web 開発と、IIS、Windows Server についての啓発活動をしております。)

ちなみに tech・Days Japan 2009 フォローアップはここ(http://blogs.msdn.com/osamum/)でやってますので興味のある方はご覧ください。

さて、今回は先回の予告通り、作成中のメモ帳に検索機能を実装していきます。

多くの人が既に御存じのとおり、多くの情報を扱う状況では、検索機能は "あれば便利" という域を越え必須とも言える機能です。

もし、検索機能の無いアプリケーションをユーザーが使用したらどうなるか?、あらためて考えるまでもないでしょう。

答えは明らかです。"ものすごくイラつく" のです。

これでは素晴らしいエクスペリエンスを得られないばかりか、精神衛生上もよろしくありません。なにより必要な情報を探すのに時間がかかってしょうがありません。

"データを扱う"という自覚のあるアプリケーションであれば検索機能は搭載すべき機能です。これはコンピューターがユーザーに返す出力が、演算結果か検索の結果であるということからも明らかです。(※もっとも広義ではすべては"演算結果"なわけですが。)

 

今回実装する機能について 

今回使用する文字列の検索ロジックは難しくありません。

実際のところ、この機能はは String クラスの持っている IndexOf メソッドに GUI を紐付ければ良いだけです。

機能実現の具体的な処理の流れとしてはこんなとこでしょう。

  1. 検索用ダイアログボックスを表示
       ↓
  2. テキストボックス内の文字列の IndexOf 関数に検索する文字列、開始位置を設定して検索する
       ↓
  3. テキストボックスのカーソルを検索対象文字の出現位置まで移動し、該当している文字列を選択状態とする
       ↓
  4. [次を検索] 機能を使用する場合に備え、検索の開始位置を "検索対象文字の出現位置 + 検索対象文字の文字数" に設定しておく
       ↓
  5. ②に戻る

この処理は [置換] 機能の実装にも使用することができます。

[置換] というと Replace 関数の方がふさわしいのでは? と考える人もあるでしょうが、Replace 関数は一度に全ての置換処理を行ってしまううえ、条件に該当したものがいくつあったか等も返さないため、インタラクティブ性を考えるとあまりお勧めできません。

ちなみに置換処理の実際の処理の流れはこんなとこでしょう。

  1. 置換用ダイアログボックスを表示
       ↓
  2. 検索対象となる文字列をユーザーの入力から取得
       ↓
  3. テキストボックス内の文字列の IndexOf 関数に検索する文字列、開始位置を設定して検索する
       ↓
  4. テキストボックスのカーソルを検索対象文字の出現位置まで移動し、該当している文字列を選択状態とする
       ↓
  5. [置換] ボタンがクリックされたら選択状態となっている文字列を置換後の文字列と入れ替える
       ↓
  6. [次を検索] 機能を使用する場合に備え、検索の開始位置を "検索対象文字の出現位置 + 検索対象文字の文字数" に設定しておく
       ↓
  7. ②に戻る

それでは実際に実装してみたいと思いますが、検索ダイアログの作成手順まで書くのは非常に大変なので、今回はあらかじめ作成しておいた検索用ダイアログをダウンロードして使用します。

前回の記事まで作業を行っていた方は以下のリンクから検索ダイアログボックス用のフォームをダウンロードしてください。

[検索ダイアログボックス用フォーム]

今回、初めて作業される方は、上記のファイルと以下のプロジェクトファイルをダウンロードして解凍してください。

[サンプルプロジェクト2]

ダウンロードした以降の作業は以下のとおりです。

 

プロジェクトへの検索ダイアログボックス用フォームの追加

  1. メモ帳のプロジェクト(※ダウンロードしたものを使用する場合は EditerWrk プロジェクト) を Visual Studio 2008 でオープン
     
  2. 画面右に表示されている [ソリューション エクスプローラ] 内でプロジェクト名を選択して右クリックし、表示されたコンテキストメニューから [追加] - [既存の項目] を選択

  3. ファイル選択ダイアログボックスが表示されるで、ダウンロードした以下の検索ダイアログボックス用のファイルを全て選択(※ドラッグすることで複数選択が可能)

  4. [追加] ボタンをクリック
    [ソリューションエクスプローラ] にファイルが追加されていることを確認してください。

  5. [ソリューションエクスプローラ] で追加されたファイル findDialog.cs を右クリックし、表示されたコンテキストメニューより [コードの表示] を選択
     
  6. コードエディタで、ネームスペースがメモ帳用に作成したプロジェクトと同じであるか確認し、違っている場合は同じ名前に書き換える
     

以上の手順でプロジェクトへの既存フォームの追加は完了です。

ここからは作成しているメモ帳のメニューに検索ダイアログボックスを呼びだすコードを記述していきます。

なお、今回ダウンロードしていただいたフォームの中には、検索ダイアログボックスを制御するコード、検索、置換処理のコードを記述してありますので、メモ帳のイベントハンドラへの呼び出しの記述を行えばすぐに使用することができます。

また、このダイアログボックス用のフォームは、今回のプロジェクト中のテキストボックスのみならず、他のプロジェクトにあるテキストボックスにも使いまわすことができます。

 

ダイアログボックスの呼び出し

作成中のメモ帳から検索用ダイアログボックス (フォーム) を呼び出すには、以下の手順でコードを実装してください。

  1. [ソリューションエクスプローラ] で Form1.cs (メモ帳用のフォーム) を右クリックし、表示されたコンテキストメニューより [コードの表示] を選択
     
  2. クラスの直下に以下の変数を定義
     
    findDialog findDlg = null;

     
    これは 検索ダイアログボックスのインスタンスを保持するための変数です。クラス全体にスコープをもつように宣言します。

  3. [ソリューションエクスプローラ] で Form1.cs (メモ帳用のフォーム) を右クリックし、表示されたコンテキストメニューより [デザイナの表示]を選択
     
  4. Form1 がフォームデザイナ内に表示されるので、Form1 のメニュー [編集] - [検索] をダブルクリック
     
  5. イベントハンドラ menuFind_Click が定義されるので、以下のようにコードを記述
     
    private void menuFind_Click(object sender, EventArgs e)
    {
       //二重起動を防止
       if (findDlg == null || findDlg.IsDisposed)
       {
          //検索ダイアログボックス用フォームのインスタンスを生成
          findDlg = new findDialog(dialogMode.Find, textBox1);
          //検索ダイアログボックスを表示
          findDlg.Show(this);
       }
    }

     

  6. Form1 がフォームデザイナ内に表示されるので、Form1 のメニュー [編集] - [置換] をダブルクリック
     
  7. イベントハンドラ menuReplace_Click が定義されるので、以下のようにコード記述
     
    private void menuReplace_Click(object sender, EventArgs e)
    {
       //二重起動を防止
       if (findDlg == null || findDlg.IsDisposed)
       {
          //検索ダイアログボックス用フォームのインスタンスを生成
          findDlg = new findDialog(dialogMode.Replace, textBox1);
          findDlg.Show(this);
       }

    ※"検索" と "置換"のコードの違いは、インスタンスを生成するメソッドの第二引数のみですので、"同じようなコードを書きたくない" という方は以下のメソッドを [検索]、[置換] メニューの共通のイベントハンドラとして割り当ててもいいでしょう。

    private void menuFindReplace(object sender, EventArgs e)
    {
       //二重起動を防止
       if (findDlg == null || findDlg.IsDisposed)
       {
          //検索ダイアログボックス用フォームのインスタンスを生成
          findDlg = new findDialog((((ToolStripMenuItem)sender).Name == "menuFind") ? dialogMode.Find : dialogMode.Replace, textBox1);
          findDlg.Show(this);
        }
    }

以上で実装は完了です。[F5] キーを押下してプロジェクトを実行し、検索機能が正しく動作するか確認してみてください。

 

うまくいかない場合は以下から検索機能の実装を済んだサンプルをダウンロードしてお試しください。

[サンプルプロジェクト3]

以降、簡単にコードの説明を行います。

 

別フォームの表示について

menuFind_Click ハンドラ、menuReplace_Click ハンドラの内容を見てください。

フォームのインスタンスを格納する変数の内容が有効であるかどうか調べ、二重表示を防止しています。

//二重表示を防止
if (findDlg == null || findDlg.IsDisposed) 

インスタンスが有効でなければ(検索ダイアログボックスが表示されていなければ) フォームのインスタンスを生成し、変数に格納します。

今回のサンプルではコンストラクタの引数として検索対象とする TextBox と、"検索用のダイアログボックスを表示するのか"、"置換用のダイアログボックスを表示するのか"の列挙型(※)の引数を渡しています。

//検索ダイアログボックス用フォームのインスタンスを生成
findDlg = new findDialog(dialogMode.Replace, textBox1); 

インスタンスの生成後、TextBox クラスが持っている Show メソッドで検索ダイアログボックス用のフォームを表示します。

//検索ダイアログボックス用フォームを表示
findDlg.Show(this);

検索用ダイアログボックスは、合致する文字列が存在した場合に、その文字列を選択状態にする必要があるため Show メソッドを使用してモーダレスな表示としていますが、一般的なダイアログボックスのように、作業中にその他のフォームに対して操作を行えないようにするには ShowDialog メソッドを使用します。

※列挙型を使用すると、指定される引数を限定することができます。今回のサンプルでは、ダイアログボックスの種別を指定するためのフラグは列挙型で、findDialog.cs の内で定義しています。
真(True)/偽(False)では賄えない二つ以上の状態を指定するのに使用すると便利です。

//[検索]用、[置換]用の列挙型(フラグ用)
public enum dialogMode
{
   Find,
   Replace

 

検索ダイアログ用 フォームについて

サンプルの検索ダイアログ フォームは、置換用と検索用 UI を兼ねています。

フォームの上には置換用 UI が配置されており、その上に検索用の UI 配置した Panel コントロールを重ねてあり、その Panel コントロールの表示を切り替えることによりフォームの用途を分けています。

Panel コントロールのように他のコントロールを包含できる機能を "コンテナ属性" と呼びます。

コンテナ属性をもったコントロールを使用すると、複数のコントロールを機能単位に整理して使用することができます。

その他、フォームに設定されている属性は以下のとおりです。これらはプロパティウィンドウで設定することができます。

属性 説明
AcceptBotton findButton ユーザーが [Enter] キーを押下した際に割り当てられるボタン
CancelBotton cancelBotton ユーザーが [Esc] キーを押下した際に割り当てられるボタン
FormBorderStyle FixedToolWindow フォームの外観と動作
StartPosition CenterParent 初めて表示されたときのフォームのポジション

 

検索のロジックについて

文字列の検索は String クラスが持っている IndexOf メソッドを使用しています。

検査用フォームに書かれているコードを簡略化すると以下のようになります。

//検索の開始位置
private int findStartIndex = 0;

private void ExecFind()
{
     //検索を行う文字列の長さを取得
     int findStringLength = findString.Length;

     findPoint = "検索対象の文書".IndexOf("検索する文字列",findStartIndex);
     if (findPoint == -1) return; //該当なし

     //見つかった文字を選択状態に
     _textBox.Select(findPoint, findStringLength);

     //TextBox が選択された文字列を表示するようキャレットを移動
     _textBox.ScrollToCaret();

     //[次を検索] ボタンをクリックされた際の検索開始位置を設定
     findStartIndex = findPoint + "findStringLength ;

     //テキストボックスにフォーカスをあてる
     _textBox.Focus();

 

親フォームの操作について

フォーム内のコントロールのスコープはフォーム内で閉じており、異なるフォームから参照式を書いて直接制御することはできません。

このサンプルでは、操作する親フォーム内の TextBox のインスタンスを受けるように TextBox 型のコンストラクタの引数とプロパティを設けています。

//処理対象となる TextBox のインスタンスを保持
private TextBox _textBox;

//[検索]用のダイアログボックスを表示するか、[置換]用のダイアログボックスを表示するかのフラグ
private dialogMode _mode;

//コンストラクタ
public findDialog(dialogMode mode, TextBox txtBox)
{
   InitializeComponent();
   _textBox = txtBox;
   Mode = mode;
}

//処理対象となる TextBox のインスタンス設定するためのプロパティ
public TextBox textBox
{
   get { return _textBox;}
   set { _textBox = value; }

 

次回はアプリケーションの状態の保存について書きたいと思います。 (ほんというと、もう web アプリの開発ネタに行きたいのですが。。)

ではまた。

 

※マイクロソフト社員のコミュニティ参加について

※このエントリは ブロガーにより投稿されたものです。朝日インタラクティブ および ZDNet Japan編集部の見解・意向を示すものではありません。
  • 7件のコメント
#1   2009-03-19 15:47:21
最初のダイアログのダウンロードの中身だと対応していない部分があります。
フルセット版の中身に入っているのを使わないと検索と置換の機能が使えません。

LINQのライブラリを外したり(VS2005用)、必要なプログラムのnamespaceに変えて
あげれば別のアプリケーションでも使い回しが出来るようですね。

次回記事でウィンドウサイズの保存を自前のXMLのファイルに書き出せるような
ことをやってもらえるとプログラムの開発に役立ちそうです。
#2 ものえおさむ   2009-04-27 17:26:03
>anonymous さん
コメントありがとうございます。

>最初のダイアログのダウンロードの中身だと対応していない部分があります。
すみません、古い方をアップロードしてしまったみたいです。

>次回記事でウィンドウサイズの保存を自前のXMLのファイルに書き出せるような
はい。実は .NET には設定値の各型を自動的にシリアライズして XML 形式で保存するという機能があります。
次回はこれについて書きますね。
ちなみに明日、ポストの予定です。
#3 初心者   2013-06-12 18:47:35
[検索ダイアログボックス用フォーム]
のリンクをクリックしたところ
このアイテムは存在しないか,使用できなくなっています
と表示されるのですがどうすればいいですか?
#4 はじめてのテキストエディタ   2015-11-01 14:27:01
私も同じように
[検索ダイアログボックス用フォーム]
のリンクをクリックしたところ、存在しなくなっているようです。
再度アップして頂けますでしょうか?

せっかくこのようなサイトが存在するので、ここでギブアップするのは勿体ありません。
#5   2016-07-30 16:19:34
ページが存在しないと出てきます
もう一度アップすることを希望します
#6 nanashi   2016-12-16 15:18:32
失礼致します。こちらのソースコードを参考にしてFind機能を作ろうとしましたが、ソースコードが既にリンク切れとなっております。
再配布していただけないでしょうか?
#7 DelFusa   2017-01-30 21:16:06
EditerWrk で検索すると、ここが出ましたよ。

シンプルなメモ帳 Wiki - OSDN
https://ja.osdn.net/users/takkii/pf/UKEditor/wiki/FrontPage

こちらからダウンロードしたら、手に入るよ。
  • 新着記事
  • 特集
  • ブログ