C# 在WPF文本框中将插入符号移动到鼠标位置

C# 在WPF文本框中将插入符号移动到鼠标位置,c#,wpf,mouseevent,C#,Wpf,Mouseevent,我有一个子类文本框,只有当用户有意点击它时,它才会获得键盘焦点(插入符号)(这是复杂控件的一部分,用户可能并不总是想要这样的行为) 因此,我必须在代码中给文本框这个焦点(使用Keyboard.focus),所以我不能依赖现有的行为来完成它 问题是,当用户单击时(我调用Keyboard.Focus),插入符号的位置没有设置。这并不直观,也有点令人沮丧,需要用户进行第二次单击操作(一旦获得键盘焦点)以将插入符号放置在他们想要的正确位置 我会使用GetCharacterIndexFromPoint,但

我有一个子类文本框,只有当用户有意点击它时,它才会获得键盘焦点(插入符号)(这是复杂控件的一部分,用户可能并不总是想要这样的行为)

因此,我必须在代码中给文本框这个焦点(使用Keyboard.focus),所以我不能依赖现有的行为来完成它

问题是,当用户单击时(我调用Keyboard.Focus),插入符号的位置没有设置。这并不直观,也有点令人沮丧,需要用户进行第二次单击操作(一旦获得键盘焦点)以将插入符号放置在他们想要的正确位置

我会使用GetCharacterIndexFromPoint,但它给了你错误的索引!如果你的鼠标超过了最后一个字符,它会在点击时把插入符号放在它的前面,在我看来,这可能比没有任何行为更令人恼火

为此,我如何获得鼠标位置并从中导出合适的插入符号位置,以模仿控件的开箱即用行为(插入符号跳到离鼠标最近的有效位置)

我以前对此进行过研究,结果一无所获

我找到了答案

这是用户EriBeh提供的解决方案2,复制如下:

        SizeF size;
        using (Graphics graphics = Grapics.FromImage(new Bitmap(1, 1)))
        {
            size = graphics.MeasureString(CustomTextBox.Text, new Font(CustomTextBox.FontFamily.ToString(),     Convert.ToSingle(CustomTextBox.FontSize), System.Drawing.FontStyle.Regular, GraphicsUnit.Pixel));
        }

        if (mousePoint.X >= size.Width) return CustomPasswordBox.Password.Length;
        else CustomTextBox.GetCharacterIndexFromPoint(mousePoint, true);
然而,解决方案并不完全正确,因为它不考虑任何填充或文本对齐。仅仅计算文本的宽度是不够的,还必须考虑文本位于控件中的位置,并相应地导出其最右点(相对于控件),然后可以测试鼠标相对于该派生值是否正确。

因此,我对其进行了调整(我的文本将始终以控件为中心):


非常感谢优秀的解决方案。

我遇到了类似的问题,因为我的文本框也可以包装,所以不能使用文本或文本框的宽度。所以我想出了(而且还没有发现任何问题)这个解决方案

MouseDownStartPosition = e.GetPosition(textBox);

//check if user clicked some exact character
var characterIndexFalse = textBox.GetCharacterIndexFromPoint(MouseDownStartPosition, false);
//get nearest character to mouse
var characterIndex = textBox.GetCharacterIndexFromPoint(MouseDownStartPosition, true);

//GetCharacterIndexFromPoint often returns last but one character position when clicked right after textbox
//so check if characterIndexFalse is -1 (text not clicked) and if nearest character is last but one, assume that 
//user clicked behind string
if ((characterIndexFalse == -1) && (characterIndex + 1 == textBox.Text.Length)) {
   characterIndex++;
}
简要说明:

GetCharacterIndexFromPoint(true)返回与字符串中最后一个字符有问题的最近字符,并选择倒数第二个字符

GetCharacterIndexFromPoint(false)返回完全单击的字符,如果未单击字符,则返回-1


所以为了解决这个问题,我检查是否没有单击任何精确的字符,最近的字符是最后一个,但我知道插入符号需要放在最后一个字符之后。

我可以在这里添加我的2美分吗

为什么不让WPF系统自己处理呢

        // Get the protected OnMouseDown method via reflection.
        var textBoxBaseType = typeof(TextBoxBase);
        var onMouseDownMethod = textBoxBaseType.GetMethod("OnMouseDown", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

        System.Diagnostics.Debug.Assert(onMouseDownMethod != null);

        Point mousePoint = Mouse.GetPosition(txtBox);
        var mea = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left);
        mea.RoutedEvent = e.RoutedEvent;    // Where e = the EventArgs parameter passed on to this method
        txtBox.SelectionLength = 0;
        onMouseDownMethod.Invoke(txtBox, new[] { mea });

请向我们展示如何使用GetCharacterIndexFromPoint.MyTextBox.CaretIndex=GetCharacterIndexFromPoint(Mouse.GetPosition(this),true);
        // Get the protected OnMouseDown method via reflection.
        var textBoxBaseType = typeof(TextBoxBase);
        var onMouseDownMethod = textBoxBaseType.GetMethod("OnMouseDown", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);

        System.Diagnostics.Debug.Assert(onMouseDownMethod != null);

        Point mousePoint = Mouse.GetPosition(txtBox);
        var mea = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Left);
        mea.RoutedEvent = e.RoutedEvent;    // Where e = the EventArgs parameter passed on to this method
        txtBox.SelectionLength = 0;
        onMouseDownMethod.Invoke(txtBox, new[] { mea });