ViewBox 使 RichTextBox 失去插入符号 [英] ViewBox makes RichTextBox lose its caret

查看:40
本文介绍了ViewBox 使 RichTextBox 失去插入符号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

RichTextBox 被放置在一个 ViewBox 中并被放大到 10 - 1000% 的不同级别.当百分比小于 100% 时,插入符号会在随机光标位置消失.

RichTextBox is placed inside a ViewBox and zoomed to various levels 10 - 1000%. At percentages less than 100%, caret disappears at random cursor locations.

我知道当一个视觉对象被缩小(压缩)时,它会丢失像素.有什么办法可以让我的光标不再丢失?

I understand that when a visual is zoomed out (compressed), it will loose pixels. Is there any way that I can stop loosing my cursor?

    <Viewbox>
        <RichTextBox Name="richTextBox1" Width="400" Height="400" />
    </Viewbox>

推荐答案

FINAL EDIT:

嘿嘿,我只是想说,你甚至可以在没有反思的情况下完成这项工作!!这不是优化的代码,我会把它留给你自己.此外,这仍然依赖于内部的东西.所以它来了:

hey there, just wanted to say, you can even get this working without reflection at all!! This is not optimized code, I'll leave that for yourself. Also this is still relying on internal stuff. So here it comes:

代码隐藏:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        rtb.LayoutUpdated += (sender, args) =>
        {
            var child = VisualTreeHelper.GetChild(vb, 0) as ContainerVisual;
            var scale = child.Transform as ScaleTransform;
            rtb.ScaleX = scale.ScaleX;
        };
    }
}

public class RTBwithVisibleCaret:RichTextBox
{
    private UIElement _flowDocumentView;
    private AdornerLayer _adornerLayer;
    private UIElement _caretSubElement;
    private ScaleTransform _scaleTransform;

    public RTBwithVisibleCaret()
    {            
        LayoutUpdated += (sender, args) =>
            {
                if (!IsKeyboardFocused) return;
                if(_adornerLayer == null)
                    _adornerLayer = AdornerLayer.GetAdornerLayer(_flowDocumentView);
                if (_adornerLayer == null || _flowDocumentView == null) return;
                if(_scaleTransform != null && _caretSubElement!= null)
                {
                    _scaleTransform.ScaleX = 1/ScaleX;
                    _adornerLayer.Update(_flowDocumentView);
                }
                else
                {
                    var adorners = _adornerLayer.GetAdorners(_flowDocumentView);
                    if(adorners == null || adorners.Length<1) return;
                    var caret = adorners[0];
                    _caretSubElement = (UIElement) VisualTreeHelper.GetChild(caret, 0);
                    if(!(_caretSubElement.RenderTransform is ScaleTransform))
                    {
                        _scaleTransform = new ScaleTransform(1 / ScaleX, 1);
                        _caretSubElement.RenderTransform = _scaleTransform;
                    }
                }
            };
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        var cthost = GetTemplateChild("PART_ContentHost") as FrameworkElement;
        _flowDocumentView = cthost is ScrollViewer ? (UIElement)((ScrollViewer)cthost).Content : ((Decorator)cthost).Child;            
    }

    public double ScaleX
    {
        get { return (double)GetValue(ScaleXProperty); }
        set { SetValue(ScaleXProperty, value); }
    }
    public static readonly DependencyProperty ScaleXProperty =
        DependencyProperty.Register("ScaleX", typeof(double), typeof(RTBwithVisibleCaret), new UIPropertyMetadata(1.0));
}

使用此 XAML:

<Window x:Class="RTBinViewBoxTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:RTBinViewBoxTest="clr-namespace:RTBinViewBoxTest" Title="MainWindow" Height="350" Width="525">
    <Viewbox Height="100" x:Name="vb">
        <RTBinViewBoxTest:RTBwithVisibleCaret Width="70" x:Name="rtb">
            <FlowDocument>
                <Paragraph>
                    <Run>long long long long long long long long long long long long long long long long long long long long long long long long text</Run>
                </Paragraph>
            </FlowDocument>
        </RTBinViewBoxTest:RTBwithVisibleCaret>
    </Viewbox>
</Window>

是的,当我看到时,我想,所有这些都可以通过视觉树访问!您还可以遍历 VisualTree 以获取 FlowDocumentView,而不是从 RichTextBox(获取 TemplateChild 所需)继承!

yeah, it got me thinking when I saw, that all these are accessible through the visual tree! Instead of inheriting from RichTextBox (which was needed to get the TemplateChild) you can also traverse the VisualTree to get to that FlowDocumentView!

原帖:

好的,让我们看看您有哪些选择:

ok, let's look at what your options are:

正如我在上面的评论中所述:完成此操作的最简单方法应该是缩放 RichTextBox 的内容,而不是将 RichTextBox 放在 ViewBox 内.如果这是一个选项,您还没有回答(还).

as stated in my comment above: the easiest way to accomplish this whould be to have RichTextBox's content zoom instead of the RichTextBox being inside a ViewBox. You haven't answered (yet) if this would be an option.

现在其他一切都会变得复杂并且或多或少有问题:

now everything else will get complex and is more or less problematic:

您可以使用 Moq 或类似的东西(想想 Moles 左右...)替换 SystemParameters.CaretWidth 的 getter 以适应对于 ViewBox 施加的 ScaleTransform.这有几个问题!第一:这些库旨在用于测试场景,不推荐用于生产.第二:您必须在 RichTextBox 实例化 Caret 之前设置该值.不过这很困难,因为您事先不知道 ViewBox 如何缩放 RichTextBox.所以,这不是一个好的选择!

you can use Moq or something similar (think Moles or so...) to replace the getter of SystemParameters.CaretWidth to accommodate for the ScaleTransform the ViewBox exerts. This has several problems! First: these Libraries are designed for use in testing scenarios and not recommended for production use. Second: you would have to set the value before the RichTextBox instantiates the Caret. That'd be tough though, as you don't know beforehand how the ViewBox scales the RichTextBox. So, this is not a good option!

第二个(不好的)选项是使用反射来获得这个漂亮的小类 System.Windows.Documents.CaretElement.您可以通过 RichTextBox.TextEditor.Selection.CaretElement(您必须使用反射,因为这些属性和类大部分是内部密封).由于这是一个装饰器,您可以在那里附加一个 ScaleTransform 来反转缩放.不过我不得不说:这是既未经测试也不推荐

the second (bad) option would be to use Reflection to get to this nice little Class System.Windows.Documents.CaretElement. You can get there through RichTextBox.TextEditor.Selection.CaretElement (you have to use Reflection as these Properties and Classes are for the most part internal sealed). As this is an Adorner you might be able to attach a ScaleTransform there that reverses the Scaling. I have to say though: this is neither tested nor recommended!

这里你的选择有限,如果我是你,我会先猜一猜!

Your options are limited here and if I were you I'd go for my first guess!

编辑:

如果你真的想沿着第二条(坏的)路线走下去,你可能会更幸运,如果你将 ScaleTransform 应用到那个装饰器的单个孩子,你可以通过 _caretElement 类型的私有字段 _caretElement代码>CaretSubElement.如果我正确阅读了该代码,那么该子元素就是您实际的 Caret Visual.主要元素似乎用于绘制选择几何.如果您真的想这样做,请在那里应用 ScaleTransform.

If you really want to get down that second (bad) route you might have more luck if you apply that ScaleTransform to that adorners single child that you can get through the private field _caretElement of type CaretSubElement. If I read that code right, then that subelement is your actual Caret Visual. The main element seems to be used for drawing selection geometry. If you really want to do this, then apply that ScaleTransform there.

编辑:

要遵循的完整示例:

XAML:

<Window x:Class="RTBinViewBoxTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Viewbox Height="100" x:Name="vb">
        <RichTextBox Width="70" Name="rtb">
            <FlowDocument>
                <Paragraph>
                    <Run>long long long long long long long long long long long long long long long long long long long long long long long long text</Run>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
    </Viewbox>
</Window>

代码隐藏:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        rtb.GotFocus +=RtbOnGotFocus;
    }

    private void RtbOnGotFocus(object s, RoutedEventArgs routedEventArgs)
    {
        rtb.LayoutUpdated += (sender, args) =>
            {
                var child = VisualTreeHelper.GetChild(vb, 0) as ContainerVisual;
                var scale = child.Transform as ScaleTransform;
                rtb.Selection.GetType().GetMethod("System.Windows.Documents.ITextSelection.UpdateCaretAndHighlight", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(
                    rtb.Selection, null); 

                var caretElement=rtb.Selection.GetType().GetProperty("CaretElement", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(rtb.Selection, null);
                if (caretElement == null)
                    return;
                var caretSubElement = caretElement.GetType().GetField("_caretElement", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(caretElement) as UIElement;
                if (caretSubElement == null) return;
                var scaleTransform = new ScaleTransform(1/scale.ScaleX, 1);
                caretSubElement.RenderTransform = scaleTransform;
            };
    }
}

这对我有用.都说了.

这篇关于ViewBox 使 RichTextBox 失去插入符号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆