iReso Style

「ブログ + 通常コンテンツ」のハイブリッド(?)なサイト

iReso Style > Program > C# Tips > C# Tips - 現在の行や文字の位置を取得

C# Tips - 現在の行番号や文字の位置を取得する

2006年4月5日 23:21

  • 2007年6月20日 ソースファイルのキーワードを色分け
  • 再利用性を考え、既存のRichTextBoxを継承し、専用のイベントを作成する。SendMessageやGetCaretPosはAPIなので、可能な限り使わない。
  • 「Code Project」のソースを元にしております。
サンプルプログラム

ポイント : カーソルの移動検出を知らせるイベントを作成する

カーソルの位置を表示するのは、カーソルが何らかの形で移動した時なので、この「移動」を知らせるイベントを作成すればよいです。 イベント名は「CursorPositionChanged」とし、このイベントを発生させるメソッドを「OnCursorPositionChanged」とします。 「RichTextBox」には「OnSelectionChanged」メソッドがあり、文字列の選択状態に変更があれば、「SelectionChanged」イベントを発生させます。

「OnSelectionChanged」メソッドは、「0」文字選択した状態、すなわち何も選択していない状態でも、カーソルが移動することにより選択位置が変わったと判断し、イベントを発生させます。 そこで、この「OnSelectionChanged」メソッドを継承し、内部で「OnCursorPositionChanged」メソッドを実行するようにすれば、カーソル移動の検出を知らせるイベントを実現出来ます。

カーソル移動を検出するイベント部

using System;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

// namespace (好きな名前でいいです。
// 親コントロールから「using」で呼び出します。)
namespace CustomControl
{
    // RichTextBoxを継承した新しいカスタムコントロールを宣言
    public class RichTextBoxCustom : System.Windows.Forms.RichTextBox
    {
        // カーソル移動の検出を知らせるイベント
        public event EventHandler CursorPositionChanged;

        // カーソル移動の検出イベントを発生させるメソッド
        protected virtual void OnCursorPositionChanged( EventArgs e )
        {
            // イベントが未発生であれば、発生させる
            if (CursorPositionChanged != null) {
                CursorPositionChanged(this, e);
            }
        }

        // 既存の「OnSelectionChanged」メソッドをオーバーライドする
        protected override void OnSelectionChanged( EventArgs e )
        {
            // 「OnCursorPositionChanged」メソッドを実行する
            OnCursorPositionChanged(e);

            // 選択範囲が1文字以上あれば、本来の「OnSelectionChanged」メソッドを実行
            if (SelectionLength > 0) {
                base.OnSelectionChanged(e);
            }
        }

後は親コントロールで「CursorPositionChanged」イベントを設定すれば、カーソル移動のたびにイベントを受け取る事出来ます。

次に、このカスタムコントロールからカーソルの位置情報を取得するプロパティを設定します。

カーソル位置の行番号を取得するのは簡単です。「RichTextBox」には、指定した文字が何行目にあるかを取得する「GetLineFromCharIndex」メソッドが 実装されていますので、それを使用すればいいでしょう。ちなみにこの「GetLineFromCharIndex」が返す値は「0」から始まる数値ですので、プラス1した数を 返すようにします。指定した文字のインデックスは「SelectionStart」メソッドで取得できます。

現在の行の文字インデックスに関しては、「GetFirstCharIndexOfCurrentLine」メソッドが良いでしょう。これは「.NET Framework version 2.0 」から追加されたメソッドで、 「.NET Framework version 1.1」にはないメソッドです。「GetFirstCharIndexOfCurrentLine」メソッドは、現在カーソルがある行の最初の文字インデックスを返します。 「SelectionStart」で現在の文字カーソル位置の文字インデックスを取得し、この値から「GetFirstCharIndexOfCurrentLine」メソッドで取得した値を減ずる事で、目的の値を取得できます。 サンプルでは、一番左にカーソルがあった場合を「1」としていますので、実際は減じた値にプラス1しています。

また、参考までにv1.1までのバージョンでの方法もコメントアウトで載せておきます。

カーソル位置の行数、及び現在の行における文字位置を取得するプロパティ

        // 現在のカーソルの行を取得
        public int CurrentLine
        {
            get
            {
                // 「GetLineFromCharIndex」は「0」から始まる数値を返すので、
                // 実際はプラス1した値を返す
                return GetLineFromCharIndex(SelectionStart) + 1;
            }
        }

        // 現在の行におけるカーソルが何文字目かを取得 (v2.0~)
        public int CurrentColumn
        {
            get {
                // 現在のカーソルの文字インデックスから現在の行の
                // 最初の文字インデックスを減じ、さらにプラス1した値を返す
                return SelectionStart - GetFirstCharIndexOfCurrentLine() + 1;
            }
        }

        /*
        // 現在の行におけるカーソルが何文字目かを取得 (~v1.1)
        public int CurrentColumn
        {
            get {
                // 現在のカーソルのリッチテキストボックスにおける座標を
                // ピクセル単位で取得する。
                Point p = GetPositionFromCharIndex(SelectionStart);
                if (p.X == 1)
                {
                    // 一番左にカーソルがあれば、「1」を返す
                    return 1;
                }
                else
                {
                    // Y座標はそのまま、X座標を一番左にすることで、
                    // 現在の行の最初の文字インデックスを取得できる。
                    // 後は同じ。
                    p.X = 1;
                    return SelectionStart - GetCharIndexFromPosition(p) + 1;
                }
            }
        }
        */
    }
}

以上で必要最低限の機能が実装されました。

呼び出し側のフォームに今回作成したリッチテキストボックスを置きます。ファイルの先頭に「using CustomRichTextBox1;」も忘れないように記述します。

// カスタマイズしたリッチテキストボックスの宣言
internal CustomRichTextBox1.CsRichTextBox1 richTextBox1;
// コンストラクタ
this.richTextBox1 = new CustomRichTextBox1.CsRichTextBox1();

後は普通に「RichTextBox」と同じです。

サイズなどのプロパティを記述している部分に追加で、以下のイベントを記述します。

// カーソルの移動を検出するイベント
this.richTextBox1.CursorPositionChanged +=
    new System.EventHandler(this.richTextBox1_CursorPositionChanged);

// 選択範囲の変更を検出するイベント
this.richTextBox1.SelectionChanged +=
    new System.EventHandler(this.richTextBox1_SelectionChanged);

さらに、メソッドを記述します。

// カーソルの移動が検出された時の動作
private void richTextBox1_CursorPositionChanged(object sender, System.EventArgs e)
{
    // 現在の行と文字位置を取得
    int line = richTextBox1.CurrentLine;
    int col = richTextBox1.CurrentColumn;

    // ステータスバーなどに表示すると良いでしょう
}

// 選択範囲の変更が検出された時の動作
private void richTextBox1_SelectionChanged(object sender, System.EventArgs e)
{
    // 例えば、以下のようにすれば、現在選択されている文字数を取得できます。
    int sel = richTextBox1.SelectionLength;

    // 表示
}
最新のコメント
プロフィール プロフィール画像

WAWATETE

やる気の波が激しいので更新頻度が・・・

飼い主はペットに似るとはこの事を言います(´Д`;)