ViewBox使得RichTextBox失去了它的插入符号

Tra*_*ife 2 wpf richtextbox

RichTextBox放置在ViewBox内并缩放到10 - 1000%的各种级别.当百分比小于100%时,插入符号在随机光标位置消失.

我知道当视觉被缩小(压缩)时,它会丢失像素.有什么方法可以让我不再失去光标吗?

    <Viewbox>
        <RichTextBox Name="richTextBox1" Width="400" Height="400" />
    </Viewbox>
Run Code Online (Sandbox Code Playgroud)

Mar*_*ter 7

最终编辑:

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

代码隐藏:

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));
}
Run Code Online (Sandbox Code Playgroud)

使用此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>
Run Code Online (Sandbox Code Playgroud)

是的,它让我思考,当我看到,所有这些都可以通过视觉树访问!您可以遍历VisualTree来访问FlowDocumentView,而不是继承RichTextBox(获取TemplateChild所需).

原帖:

好吧,让我们来看看你的选择是什么:

正如我在上面的评论中所述:实现这一目标的最简单方法是让RichTextBox的内容缩放而不是ViewBox中的RichTextBox.如果这是一个选项,你还没有回答.

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

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

第二个(坏)选项是使用Reflection来获得这个漂亮的小类System.Windows.Documents.CaretElement.你可以通过RichTextBox.TextEditor.Selection.CaretElement(你必须使用Reflection,因为这些属性和类大部分都是internal sealed).由于这是一个Adorner,您可以在那里附加一个可以反转Scaling的ScaleTransform.我不得不说:这既不经过测试也没有推荐!

你的选择在这里是有限的,如果我是你,我会去我的第一个猜测!

编辑:

如果您真的想要了解第二条(不良)路线,那么如果您将ScaleTransform应用于您可以通过_caretElement类型的私有字段获得的单个子项,则可能会有更多运气CaretSubElement.如果我正确阅读该代码,那么该子元素就是您的实际Caret Visual.主要元素似乎用于绘制选择几何.如果你真的想这样做,那么在那里应用ScaleTransform.

编辑:

完整示例如下:

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>
Run Code Online (Sandbox Code Playgroud)

代码隐藏:

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;
            };
    }
}
Run Code Online (Sandbox Code Playgroud)

这对我有用.一切都说.