在 UWP 下,GetRectFromCharacterIndex 不会 return 值调整为控件的样式

Under UWP, GetRectFromCharacterIndex does not return values adjusted to the style of the control

在 UWP(相对于 WPF)中使用 GetRectFromCharacterIndex 时,结果是相对于可以输入文本的位置的绝对偏移量,而不是在控件内部。

例如下面的XAML:

<TextBox x:Name="noPadding" Margin="0,0,0,20" Text="aaa"/>

当您调用 GetRectFromCharacterIndex(0) 时,returns UWP 和 WPF 上的不同 Rect.Left 值。

WPF:Rect.Left == 3 UWP:Rect.Left == 0

当您重新设计控件样式或以其他方式改变控件的外观时,差异会变得更加明显:

<TextBox x:Name="withPadding" Padding="60,0,0,0" Margin="0,0,0,20" Text="aaa"/>

WPF:Rect.Left == 63 UWP:Rect.Left == 0

如何获取角色在控件上的实际位置?

注意:我知道我可以通过计算 TextBox 内的 TextView 的位置来破解它。但我试图了解支持的方法是什么。

因此鉴于尚未得到适当支持,以下是我的解决方法(如果有人找到 "supported" 解决方案,我将取消标记我的答案并标记那个)

这个扩展方法似乎可以解决问题:

    public static Rect GetRelativeRectFromCharacterIndex(this TextBox textBox, int charIndex, bool trailingEdge)
    {
        var caret = textBox.GetRectFromCharacterIndex(charIndex, trailingEdge);

        // Hack: UWP does not properly return the location compared to the control, so we need to calculate it.
        // 
        var scroller = textBox.GetDescendants().OfType<ScrollContentPresenter>().FirstOrDefault();
        var transform = scroller.TransformToVisual(textBox);
        transform.TryTransform(new Point(caret.Left, caret.Top), out var topLeft);
        transform.TryTransform(new Point(caret.Right, caret.Bottom), out var bottomRight);

        caret = new Rect(topLeft, bottomRight);
        return caret;
    }

然后,您需要 GetDescendants():

    public static IEnumerable<DependencyObject> GetDescendants(this DependencyObject container)
    {
        var stack = new Stack<DependencyObject>();
        stack.Push(container);

        while (stack.Count > 0)
        {
            var item = stack.Pop();
            var count = VisualTreeHelper.GetChildrenCount(item);
            for (int i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(item, i);
                yield return child;
                stack.Push(child);
            }
        }
    }