2011-03-03 61 views
2

RichTextBox放置在ViewBox中并放大到10 - 1000%的各个级别。在百分比低于100%的情况下,脱字符随机光标位置消失。ViewBox使RichTextBox失去它的插入

我明白,当视觉缩小(压缩)时,它会失去像素。有什么办法可以停止放松我的光标吗?

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

是否可以将RichTextBox的内容缩放到各个级别?像增加和减少与变量成比例的字体大小和页面宽度? – 2011-03-05 21:10:10

+1

也许是对你的目标是什么的解释,以防有人可以想出一种不同的方法来处理它,而不是使用'ViewBox' +'RichTextBox'? – 2011-03-16 13:42:36

+0

您定位的是什么版本的.NET/WPF? .NET/WPF 4? – CodeNaked 2011-03-17 16:18:42

回答

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> 

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

原帖

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

在我上面的评论中指出:要完成这个对子级是有RichTextBox的内容变焦的最简单的方法,而不是RichTextBox在ViewBox中。你还没有回答(如果这是一个选项)。

现在一切将变得复杂,并或多或少存在问题:

可以使用Moq或类似的东西(认为痣左右...),以取代SystemParameters.CaretWidth,吸气,以适应ScaleTransform视框发挥。这有几个问题!第一:这些库是专门用于测试场景和不推荐用于生产使用。其次:您必须在RichTextBox实例化插入符之前设置该值。这很难,因为您事先并不知道ViewBox如何缩放RichTextBox。所以,这不是一个好的选择!

第二个(坏)选项将使用反射来到这个漂亮的小类System.Windows.Documents.CaretElement。您可以通过RichTextBox.TextEditor.Selection.CaretElement(您必须使用Reflection,因为这些属性和类大部分为internal sealed)。因为这是一个装饰者,所以你可能会附加一个ScaleTransform来反转Scaling。我不得不说:这是既没有测试也没有推荐

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

编辑

如果你真的想坐下即第二(坏的)途径,如果您应用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> 

代码隐藏:

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; 
      }; 
    } 
} 

这对我的作品。一切都在说。

+0

缩放内容,而不是将RTB放入ViewBox中 - 工作太多! – Trainee4Life 2011-03-20 07:57:59

+0

我看了第二个选项。这个解决方案的问题在于,装饰器的大小与实际大小相同(并且不等于实际的大小),因此ScaleTransform也会改变插入符号的位置。 – Trainee4Life 2011-03-20 08:03:03

+0

@ Trainee4Life请看我的编辑。我仍然认为缩放内容会是更好的方法。 – 2011-03-20 09:44:38

1

AFAIK你不能真正解决这个问题。 ViewBox在封面下使用ScaleTransform,ScaleTransform在缩小时隐藏某些行,因为它无法显示所有内容。该ScaleTransform没有使用非常先进的算法,做规模,(这只是它尽可能以最快的方式),我不认为你可以改变的..