Retrieve the Caret Position by Using an Extended TextBox Class

CodeGuru Editor's Comment: This article uses the Win32 API from C#. Generally, you should avoid calling such unmanaged code from a managed (.NET) application. This is, however, an interesting example of calling the API from .NET.


Sometimes, we need to know the caret position in a TextBox control; for example, in editor programs. The following ExtTextBox class extends the TextBox class with two read-only properties. The GetCaretXYPosition returns a Point struct with the (X,L) coordinates of the current position of the caret. The GetCaretLCPosition returns a CharPoint custom struct with the (L, C) coordinates of the position of the caret, where L is the one-based line number and C is the one-based column number.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing;
namespace TextBoxUtils
{
   /// <summary>
   /// struct to handle the caret (L, C) co-ordinates
   /// L = one-based line number of the line containing the caret
   /// C = one-based column number in the line containing the caret
   /// </summary>
   public struct CharPoint
   {
      private int l, c;

      public static readonly CharPoint Empty;

      static CharPoint()
      {
         CharPoint.Empty = new CharPoint();
      }

      public CharPoint(int l, int c)
      {
         this.l = l;
         this.c = c;
      }

      public override string ToString()
      {
         return(String.Format("{{L={0}, C={1}}}", this.l, this.c));
      }

   }

   public class ExtTextBox: TextBox
   {
      [DllImport("user32")] private static extern IntPtr
                            SendMessage(HandleRef hWnd, int msg,
                                        int wParam, int lParam);
      [DllImport("user32")] private static extern int
                            GetCaretPos(ref Point lpPoint);
      private int EM_LINEINDEX    = 0xbb;
      private int EM_LINEFROMCHAR = 0xc9;
      private int EM_GETSEL       = 0xb0;

      /// <summary>
      /// Gets the caret current (X, Y) position.
      /// </summary>
      /// <value>
      /// Point struct
      /// </value>
      public Point GetCaretXYPosition
      {
         get
         {
            Point pt = Point.Empty;
            // get a Point struct with the caret current (X, Y)
            // position
            GetCaretPos(ref pt);
            // return the Point struct with the caret current
            // (X, Y) position
            return pt;
         }
      }

      /// <summary>
      /// Gets the caret current (L, C) position.
      /// </summary>
      /// <value>
      /// CharPoint struct
      /// </value>
      public CharPoint GetCaretLCPosition
      {
         get
         {
            CharPoint cpt = CharPoint.Empty;
            // save the handle reference for the ExtToolBox
            HandleRef hr = new HandleRef(this, base.Handle );
            // Send the EM_LINEFROMCHAR message with the value of
            // -1 in wParam.
            // The return value is the zero-based line number 
            // of the line containing the caret.
            int l = (int)SendMessage(hr,EM_LINEFROMCHAR, -1, 0);
            // Send the EM_GETSEL message to the ToolBox control.
            // The low-order word of the return value is the
            // character position of the caret relative to the
            // first character in the ToolBox control,
            // i.e. the absolute character index.
            int sel = (int)SendMessage(hr, EM_GETSEL,0, 0);
            // get the low-order word from sel
            int ai  = sel & 0xffff; 
            // Send the EM_LINEINDEX message with the value of -1
            // in wParam.
            // The return value is the number of characters that
            // precede the first character in the line containing
            // the caret.
            int li = (int)SendMessage(hr,EM_LINEINDEX, -1, 0);
            // Subtract the li (line index) from the ai
            // (absolute character index),
            // The result is the column number of the caret position
            // in the line containing the caret.
            int c = ai - li;
            cpt = new CharPoint(l+1,c+1);
            // Add 1 to the l and c since these are zero-based.
            // Return a CharPoint with the caret current (L,C)
            // position
            return new CharPoint(l+1,c+1);
         }
      }
   }
}


Downloads