DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

TextBox(またはRichTextBox)の現在のカレット位置(行と列)を取得する

テキストボックス(またはリッチテキストボックス)内で現在カレットのある行と列を取得する方法を紹介します。

論理的に行を数える

行を理論的に数えるとは、改行文字までを一行として数える方法です。テキストボックスのWordWrapプロパティがTrueの場合は、長い行は右側で折り返されますが、改行文字で折り返されるのでない限り、一行として数えます。

行を論理的に数える場合は、次のようにラインフィードを数えることで計算できます。ここでは、テキストボックス"TextBox1"の現在のカレット位置を計算しています。

補足:リッチテキストボックスでは改行文字がラインフィードのみとなりますので、キャリッジリターンを数える方法ではできません。
VB.NET
コードを隠すコードを選択
'文字列
Dim str As String = TextBox1.Text
'カレットの位置を取得
Dim selectPos As Integer = TextBox1.SelectionStart

'カレットの位置までの行を数える
Dim row As Integer = 1
Dim startPos As Integer = 0
Dim endPos As Integer
While (True)
    endPos = str.IndexOf(vbLf, startPos)
    If (endPos < 0 Or endPos >= selectPos) Then
        Exit While
    End If
    startPos = endPos + 1
    row += 1
End While

'列の計算
Dim col As Integer = selectPos - startPos + 1

'結果を表示
Console.WriteLine("行:{0} 列:{1}", row, col)
C#
コードを隠すコードを選択
//文字列
string str = TextBox1.Text;
//カレットの位置を取得
int selectPos = TextBox1.SelectionStart;

//カレットの位置までの行を数える
int row = 1, startPos = 0;
for (int endPos = 0;
    (endPos = str.IndexOf('\n', startPos)) < selectPos && endPos > -1;
    row++)
{
    startPos = endPos + 1;
}

//列の計算
int col = selectPos - startPos + 1;

//結果を表示
Console.WriteLine("行:{0} 列:{1}", row, col);

物理的に行を数える

行を物理的に数える場合は、テキストボックスのWordWrapプロパティがTrueで、右側で折り返されている場合でも、2行として数えます。

行を物理的に数える場合は、SendMessage関数を使用して取得します。

次の例では、Button1をクリックすることにより、物理的な現在のカレットの位置を表示しています。

VB.NET
コードを隠すコードを選択
<System.Runtime.InteropServices.DllImport("User32.Dll")> _
Private Shared Function SendMessage( _
    ByVal hWnd As IntPtr, _
    ByVal Msg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Integer) As Integer
End Function
Private Const EM_LINEINDEX As Integer = &HBB
Private Const EM_LINEFROMCHAR As Integer = &HC9

'Button1のクリックイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
    '現在の行を取得する
    Dim row As Integer = SendMessage( _
        TextBox1.Handle, EM_LINEFROMCHAR, -1, 0) + 1

    '現在の列を取得する
    Dim lineIndex As Integer = SendMessage( _
        TextBox1.Handle, EM_LINEINDEX, -1, 0)
    Dim col As Integer = TextBox1.SelectionStart - lineIndex + 1

    Console.WriteLine("行:{0} 列:{1}", row, col)
End Sub
C#
コードを隠すコードを選択
[System.Runtime.InteropServices.DllImport("User32.Dll")]
private static extern int SendMessage(
    IntPtr hWnd,int Msg,int  wParam,int  lParam);
private const int EM_LINEINDEX = 0xBB;
private const int EM_LINEFROMCHAR=0xC9;

//Button1のクリックイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //現在の行を取得する
    int row = SendMessage(
        TextBox1.Handle, EM_LINEFROMCHAR, -1, 0) + 1;

    //現在の列を取得する
    int lineIndex = SendMessage(
        TextBox1.Handle, EM_LINEINDEX, -1, 0);
    int col = TextBox1.SelectionStart - lineIndex + 1;

    Console.WriteLine("行:{0} 列:{1}", row, col);
}

RichTextBoxまたは、.NET Framework 2.0以降の場合

RichTextBoxの物理的な行は、GetLineFromCharIndexメソッドで取得できます。GetLineFromCharIndexメソッドは、.NET Framework 2.0からは、TextBox(TextBoxBase)クラスにも追加されました。

GetLineFromCharIndexメソッドは、1行目を「0」とします。

VB.NET
コードを隠すコードを選択
'RichTextBox1の現在の物理行を表示する
Console.WriteLine(
    RichTextBox1.GetLineFromCharIndex(RichTextBox1.SelectionStart))
C#
コードを隠すコードを選択
//RichTextBox1の現在の物理行を表示する
Console.WriteLine(
    RichTextBox1.GetLineFromCharIndex(RichTextBox1.SelectionStart));

しかしこの方法では、自動的に折り返されている行の一番最後にカレットがあるときは、次の行の番号を返してしまいます。

  • 履歴:
  • 2006/11/12 「論理的に行を数える」のVB.NETのコードを修正。
  • 2007/1/16 GetLineFromCharIndexメソッドの説明をより詳しくした。
  • 2009/10/11 誤字修正。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。