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

时间:2011-03-03 12:15:43

标签: wpf richtextbox

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

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

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

2 个答案:

答案 0 :(得分: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));
}

使用此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所需)!

原帖

好的,让我们来看看你的选择:

如上面的评论中所述:实现此目的的最简单方法是使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应用于那些可以通过类型为{{1}的私人字段_caretElement的单个子项,那么您可能会有更多的运气}。如果我正确阅读该代码,那么该子元素就是您的实际Caret Visual。主要元素似乎用于绘制选择几何。如果你真的想这样做,那么在那里应用ScaleTransform。

修改

完整的示例:

XAML:

CaretSubElement

代码隐藏:

<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>
这对我有用。一切都说了。

答案 1 :(得分:1)

AFAIK你无法真正解决这个问题。 ViewBox正在使用ScaleTransform,缩小时,ScaleTransform将隐藏某些行,因为它无法显示所有内容。 ScaleTransform没有使用非常先进的算法来进行扩展(它只是以最快的方式完成)并且我认为你不能改变它......