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



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

因此,我必须在代码中(使用 Keyboard.Focus)为文本框提供此焦点,因此我不能依赖现有行为来执行此操作。

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

我会使用GetCharacterIndexFromPoint,但它给了你错误的索引!如果您的鼠标超出最后一个字符,它会在单击时将插入符号放在其前面,在我看来,这比根本没有行为更令人恼火。

为此,如何以模拟控件的开箱即用行为(插入符号跳转到离鼠标最近的有效位置)的方式获取鼠标位置并从中派生合适的插入符号位置?

我以前研究过这个,结果是空白的。

我遇到了类似的问题,当我的文本框也可以换行时,因此无法使用文本或文本框的宽度。所以我想通了(还没有发现任何问题)这个解决方案

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

因此,为了解决此问题,我检查是否没有单击确切的字符,并且最近的字符是最后一个字符,但我知道插入符号需要放在最后一个字符之后。

我找到了答案。

这是用户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);

但是,该解决方案并不完全正确,因为它不考虑任何填充或文本对齐。仅仅计算文本的宽度是不够的,还必须考虑文本在控件中的位置并相应地派生其最右边的点(相对于控件),然后可以测试鼠标是否针对该派生值进一步向右。

因此,我对此进行了调整(我的文本将始终在控件中居中):

SizeF size;
using (var graphics = Graphics.FromImage(new Bitmap(1, 1)))
{
size = graphics.MeasureString(Text, new Font(FontFamily.ToString(), Convert.ToSingle(FontSize), System.Drawing.FontStyle.Regular, GraphicsUnit.Pixel));
}
var mousepos = Mouse.GetPosition(this);
var textXLocationAtRightmostEdge = (ActualWidth / 2) + (size.Width / 2);
CaretIndex = mousepos.X >= textXLocationAtRightmostEdge ? Text.Length : GetCharacterIndexFromPoint(mousepos, true);

卓越的解决方案受到赞赏。

我可以在这里添加我的 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 });

最新更新