目次

テキストボックスのキャレットのある位置の行と位置を取得する - C#

テキストボックスでキャレットのある位置の行と位置を取得するコードを紹介します。

UI

下図のUIを作成します。TextBoxを2つ、Buttonを4つ配置します。TextBoxはMultilineプロパティをTrueに設定します。ScrollBarsプロパティを"Both"に設定します。

コード

以下のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace GetCaretIndex
{
  public partial class FormMain : Form
  {
    const int EM_LINEINDEX = 0xBB;
    const int EM_LINEFROMCHAR = 0xC9;

    [DllImport("User32.Dll")]
    private static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);


    public FormMain()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      int charpos = textBox1.SelectionStart;
      textBox2.Text += string.Format("SelectionStart - {0:d}\r\n", charpos);
    }

    private void button2_Click(object sender, 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;

      textBox2.Text += string.Format("Row/Col - {0:d}/{1:d}\r\n", row,col);
    }

    private void button3_Click(object sender, EventArgs e)
    {
      int charpos = textBox1.SelectionStart;
      int linepos = textBox1.GetLineFromCharIndex(charpos);
      textBox2.Text += string.Format("line - {0:d}\r\n", linepos);
    }

    private void button4_Click(object sender, EventArgs e)
    {
      string text = textBox1.Text;

      int caretPos = textBox1.SelectionStart;

      int row = 1;
      int pos = 0;

      for (int ep = 0; ep < caretPos; ep++) {
        ep = text.IndexOf('\n', pos);
        if (ep < caretPos && -1 < ep) {
          row++;
        }
        else {
          break;
        }
        pos = ep + 1;
      }

      int col = caretPos - pos + 1;
      textBox2.Text += string.Format("Row/Col - {0:d}/{1:d}\r\n", row, col);
    }
  }
}

先頭からのキャレット位置を取得する

解説

ドキュメントの先頭から何文字目にキャレットがあるのかを取得します。キャレットの位置はTextBoxクラスのSelectionStartプロパティで取得できます。
private void button1_Click(object sender, EventArgs e)
{
  int charpos = textBox1.SelectionStart;
  textBox2.Text += string.Format("SelectionStart - {0:d}\r\n", charpos);
}

実行結果

プロジェクトを実行します。下図の画面が表示されます。


上部のテキストボックスに文字を入力します。キャレットを4行目のWとKの間に置き、[button1]をクリックします。下図の結果が表示されます。キャレットがテキストボックスの先頭にある場合は"0"、N文字目の後ろにあるときは"N"を返します。



物理的なキャレットのある行、列を取得する

解説

ワードラップに関係なく物理的にキャレットのある行や列を取得します。この場合は行はTextBoxにEM_LINEFROMCHARメッセージを送信するとその戻り値として取得できます。先頭行は"0"になります。
また、列はTextBoxにEM_LINEINDEXメッセージを送信すると戻り値でキャレットのある行の先頭までの文字数が取得できるため、ドキュメントの先頭からキャレットの位置までの文字数"TextBox.SelectionStart"との差で何列目か取得できます。
private void button2_Click(object sender, 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;

  textBox2.Text += string.Format("Row/Col - {0:d}/{1:d}\r\n", row,col);
}

実行結果

プロジェクトを起動し、上部のテキストボックスに文字を入力します。キャレットを3行目の"G"と"O"の間に置き[button2]をクリックします。キャレットの行と列が表示されます。


別の文章を入力します。キャレットを2行目の"理"と"し"の間に置き[button2]をクリックします。本来ワードラップしていなければこの行は1行目になりますが、テキストボックスの物理的な位置では2行目の9文字目のため、その数が下部のテキストボックスに表示されます。


キャレットのある行のみを取得する場合

解説

キャレットのある行数のみの取得の場合はTextBoxのGetLineFromCharIndex() メソッドを用いることで取得できます。
private void button3_Click(object sender, EventArgs e)
{
  int charpos = textBox1.SelectionStart;
  int linepos = textBox1.GetLineFromCharIndex(charpos);
  textBox2.Text += string.Format("line - {0:d}\r\n", linepos);
}

実行結果

プロジェクトを起動し、文章を入力します。キャレットを3行目の"G"と"O"の間に置き[button2]をクリックします。下図の結果が表示されます。


キャレットを先頭の行に置いた場合(キャレットを1行目の"G"と"U"の間)、行は0になります。


下図の、キャレットを7行目の"。"の後ろに置いた場合、行は"6"と表示されます。本来ワードラップしていなければこの行は3行目になりますが、GetLineFromCharIndex()で取得した値は物理的な位置の行を返します。

改行を数える場合

解説

キャレットの位置より手前にある改行の個数を数え行数を出します。また、ドキュメントの先頭からのキャレットの位置から最後の改行の文字数を引くことでキャレットのある列数を求めます。
private void button4_Click(object sender, EventArgs e)
{
  string text = textBox1.Text;

  int caretPos = textBox1.SelectionStart;

  int row = 1;
  int pos = 0;

  for (int ep = 0; ep < caretPos; ep++) {
    ep = text.IndexOf('\n', pos);
    if (ep < caretPos && -1 < ep) {
      row++;
    }
    else {
      break;
    }
    pos = ep + 1;
  }

  int col = caretPos - pos + 1;
  textBox2.Text += string.Format("Row/Col - {0:d}/{1:d}\r\n", row, col);
}

実行結果

プロジェクトを実行し、下図の文字を入力します。キャレットを4行目の"A"と"W"の間に置き、[button4]を押します。下図の結果が表示されます。


下図の文章を入力し、6行目の"。"と"そ"の間にキャレットを置き、[button4]をクリックします。下部のテキストボックスに下図の結果が表示されます。ワードラップされているため、本来この行は3行目になり、ワードラップされていない場合の行数と列数が表示されています。


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2012-06-26
iPentec all rights reserverd.