简体   繁体   中英

Get cursor position of winforms textbox

How do I get the current cursor (caret) position of a winforms textbox in .NET? SelectionStart only returns the start of the selected text (left side of the selection). Means this value is wrong if the cursor is at the right side of the selection.


To clarify: In .NET TextBox SelectionStart points to the left side of an selection, also when the caret is at the right side of the selection. This means in both pictures SelectionStart is 2, but the caret position is 2 in the first picture and 7 in the right picture.

在此处输入图像描述

As already stated, the SelectionStart property is not reliable to get the actual CARET position in a TextBox with a selection active. This is caused by the fact that this property points always at the selection start (clue: the name doesn't lie) and depending on how you select the text with the mouse the caret could be positioned on the LEFT or RIGHT side of the selection.

This code (tested with LinqPAD) shows an alternative

public class WinApi
{
    [DllImport("user32.dll")]
    public static extern bool GetCaretPos(out System.Drawing.Point lpPoint);
}

TextBox t = new TextBox();
void Main()
{
    Form f = new Form();
    f.Controls.Add(t);
    Button b = new Button();
    b.Dock = DockStyle.Bottom;
    b.Click += onClick;
    f.Controls.Add(b);
    f.ShowDialog();
}

// Define other methods and classes here
void onClick(object sender, EventArgs e)
{
    Console.WriteLine("Start:" + t.SelectionStart + " len:" +t.SelectionLength);
    Point p = new Point();
    bool result = WinApi.GetCaretPos(out p);
    Console.WriteLine(p);
    int idx = t.GetCharIndexFromPosition(p);
    Console.WriteLine(idx);
}

The API GetCaretPos returns the point in client coordinates where the CARET is. You could return the index of the character after the position using the managed method GetCharIndexFromPosition . Of course you need to add a reference and a using to System.Runtime.InteropServices .

Not sure if there is some drawback to this solution and waiting if someone more expert can tell us if there is something wrong or unaccounted for.

With the answer from Steve this is now my solution:

[DllImport("user32")]
private extern static int GetCaretPos(out Point p);

...

// get current caret position
Point caret;
GetCaretPos(out caret);
int caretPosition = tbx.GetCharIndexFromPosition(caret);

Additionally (not part of my question) I am able to set the caret and text selection with following code. There also is a SetCaret function in user32.dll which didn't work for me. But surprisingly the Select() function supports negative values for selection length.

// determine if current caret is at beginning
bool caretAtBeginning = tbx.SelectionStart == caretIndex;

...

// set text selection and caret position
if (caretAtBeginning)
    tbx.Select(selStart + selLength, -selLength);
else
    tbx.Select(selStart, selLength);

Note: This post was extracted from the Question and posted on the OP's behalf.

If you want the left side of the selection, you can use:

int index = textBox.SelectionStart;

If you want the right side of the selection, you can use:

int index = textBox.SelectionStart + textBox.SelectionLength;

If you need to know which direction the text was selected then you could try tracking mouse down and mouse up events and comparing the cursor positions when the events fired.

For example:

// On mouse down event.
int mouseDownX = MousePosition.X;

// On mouse up event.
int mouseUpX = MousePosition.X;

// Check direction.
if(mouseDownX < mouseUpX)
{
    // Left to Right selection.
    int index = textBox.SelectionStart;   
}
else
{
    // Right to Left selection.
    int index = textBox.SelectionStart + textBox.SelectionLength;
}

This may come in use to someone:

int lastSelectionStart = 0;

int getCaretPosition()
{
    if (tbx.SelectionLength == 0)
    {
        lastSelectionStart = tbx.SelectionStart;
        return lastSelectionStart;
    }
    else
    {
        if (tbx.SelectionStart == lastSelectionStart)
        {
            return tbx.SelectionStart + tbx.SelectionLength;
        }
        else
        {
            return lastSelectionStart - tbx.SelectionLength;
        }
    }
}

When you click the mouse or move an arrow key button inside a TextBox, your cursor will be moved to a new location and the location will be recorded in this variable.

textBox1.SelectionStart

The value is character count starting from 0 at the beginning of the TextBox to the location of your cursor. If you carefully code it, you'll make it work correctly. To highlight selection, you should use this function to avoid confusion.

textBox1.Select(start, length);

where

int start = textBox1.SelectionStart;
int length = textBox1.SelectionStart+textBox1.SelectionLength;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM