获取WinForms文本框的光标位置

8
如何在.NET中获取winforms文本框的当前光标位置?SelectionStart仅返回所选文本的起始位置(选择区域的左侧)。这意味着如果光标位于选择区域的右侧,则此值是错误的。
为了澄清:在.NET中,TextBox的SelectionStart指向选择区域的左侧,即使插入符位于选择区域的右侧也是如此。这意味着在两张图片中,SelectionStart都是2,但第一张图片中插入符位置为2,在右侧图片中插入符位置为7。

enter image description here


3
“如果光标在选择范围的右侧,这意味着该值是错误的”的意思是什么?SelectionStart + SelectionLength 有帮助吗? - Sriram Sakthivel
3
对于那些(OP)不理解自己问题的人,请先阅读以下内容:当你选择文本并输入文字时,会发生什么?它会插入在选定文本的开头,因此光标位置必须在那里。无论你选择的方向是哪个,光标都将位于选定文本的开头。 - musefan
@musefan:区别在于:我不想插入文本,我想知道数字索引。 - user1027167
"SelectionStart仅返回所选文本的开头(选择的左侧)" - 不是的。它返回插入符号的当前位置,如果未选择任何文本,或在某些文本被选择时返回选择起始位置。因此,您可以在任何情况下都使用它来计算位置..." - Fabjan
3
什么?光标在技术上始终位于选择范围的左侧,这就是为什么要使用SelectionLength来获取右侧位置的原因。 - Kilazur
显示剩余9条评论
5个回答

9

正如已经提到的那样,使用SelectionStart属性在TextBox中获得有选择文本时光标的真实位置是不可靠的。这是由于该属性始终指向选定文本的起始位置(提示:名称不会说谎),并且根据您使用鼠标选择文本的方式,光标可能位于选择的左侧或右侧。

下面的代码(在LinqPAD中测试)展示了另一种方法:

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);
}

API GetCaretPos 返回光标所在位置的客户端坐标。您可以使用托管方法 GetCharIndexFromPosition 返回该位置后字符的索引。当然,您需要添加对 System.Runtime.InteropServices 的引用和使用。不确定此解决方案是否存在一些缺点,等待更专业的人士告诉我们是否存在错误或未考虑到的问题。

很不幸,这只在文本框获得焦点时有效。因此,如果需要未获得焦点的文本框的插入符位置,则此方法无法使用(除非您交换当前焦点以获取位置,但这是一种丑陋的hack)。 - Domi

1

有了Steve的答案,现在这是我的解决方案:

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

...

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

此外(不是我的问题的一部分),我可以使用以下代码设置插入符号和文本选择。在user32.dll中也有一个SetCaret函数,但对我无效。但令人惊讶的是,Select()函数支持负数作为选择长度。
// 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);

注意:此帖子是从问题中提取的,并代表原作者发布。

0
当你在TextBox内点击鼠标或按箭头键时,光标将移动到新位置,并将该位置记录在此变量中。
textBox1.SelectionStart

该值是从TextBox开头到光标位置的字符计数,从0开始。如果您仔细编写代码,它将正确工作。为了突出选择,您应该使用此功能以避免混淆。

textBox1.Select(start, length);

在哪里

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

0
这可能对某些人有用:
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;
        }
    }
}

0
如果你想要选择的左侧,可以使用以下代码:
int index = textBox.SelectionStart;

如果你想要选择区域的右侧,可以使用以下代码:

int index = textBox.SelectionStart + textBox.SelectionLength;

如果您需要知道所选文本的方向,则可以尝试跟踪鼠标按下和鼠标释放事件,并比较事件触发时的光标位置。
例如:
// 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;
}

好的,那可能是一个解决方法。所以我还要跟踪键盘。如果没有更好的解决方案,我就得采取这个方法。感谢您提供的这个想法。 - user1027167

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接