WPF:在TextBlock之上绘图

时间:2010-06-24 03:22:16

标签: wpf performance drawing textblock frameworkelement

我希望能够绘制到TextBlock的顶部,并找到了一种方法来执行此操作,但是一旦它在那里我就无法删除它。这是代码。

   public class DerivedTextBlock : TextBlock {

      public Boolean DrawExtra {
         get { return (Boolean)GetValue(DrawExtraProperty); }
         set { SetValue(DrawExtraProperty, value); }
      }

      // Using a DependencyProperty as the backing store for DrawExtra.  This enables animation, styling, binding, etc...
      public static readonly DependencyProperty DrawExtraProperty =
          DependencyProperty.Register("DrawExtra", typeof(Boolean), typeof(DerivedTextBlock), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsArrange));

      public DrawingVisual DrawingVisual { get; set; }

      public DerivedTextBlock() {
         DrawingVisual = this.CreateDrawingVisualRectangle();
      }

      protected override int VisualChildrenCount {
         get {
            //if we want to draw our extra info, add one to
            // our visualChildrenCount, usually with a textblock it is 0
            if (DrawExtra) {
               return base.VisualChildrenCount + 1;
            }
            else {
               return base.VisualChildrenCount;
            }
         }
      }

      protected override Visual GetVisualChild(int index) {
         return DrawingVisual;
      }

      // Create a DrawingVisual that contains a rectangle.
      private DrawingVisual CreateDrawingVisualRectangle() {
         DrawingVisual drawingVisual = new DrawingVisual();

         // Retrieve the DrawingContext in order to create new drawing content.
         DrawingContext drawingContext = drawingVisual.RenderOpen();

         // Create a rectangle and draw it in the DrawingContext.
         Rect rect = new Rect(new Point(10.0, 0), new Size(10.0 / 2.0, 10));
         drawingContext.DrawRectangle(Brushes.LightBlue, (Pen)null, rect);

         // Persist the drawing content.
         drawingContext.Close();

         return drawingVisual;
      }
   }

我想这样做的原因:我们有一个包含大量单元格的数据网格,每个单元格都显示文本。我们在单元格上显示一些验证信息,我们通过使用带有文本块的模板和网格中的某些路径来实现此目的。这会增加额外的元素到可视化树,当我们不得不重绘(加载,切换窗口或排序)时,可视化树中的元素越多,所需的时间就越长。当它只是一个文本块时,比使用网格的控件快约1/3 - 1/2。所以我们想在文本框的顶部绘制我们的验证内容。

1 个答案:

答案 0 :(得分:2)

你的问题是:

  1. GetVisualChild()应该返回base.GetVisualChild(index),除非index == base.VisualChildrenCount。
  2. 当DrawingExtra变为true或DrawingVisual发生更改时,您忘记调用AddVisualChild()
  3. 当DrawingExtra变为false或DrawingVisual发生更改时,您忘记调用RemoveVisualChild()
  4. 您可以通过在DrawingExtra上设置PropertyChangedCallback并将代码添加到DrawingVisual的setter来修复#2和#3。

    说明:AddVisualChild()调用实际上将视觉添加到树中。发生的事情是,由于GetVisualChild()中的错误,您的视觉被“意外”发现并显示,但它没有正确链接到可视树中,因此您将遇到许多问题。

    <强>更新

    我按照上面的描述编辑了你的代码,它运行得很好。以下是更改:

    ...
          {
            PropertyChangedCallback = (obj, e) =>
              {
                var textBlock = (DerivedTextBlock)obj;
                if((bool)e.OldValue) textBlock.RemoveVisualChild(textBlock.DrawingVisual);
                if((bool)e.NewValue) textBlock.AddVisualChild(textBlock.DrawingVisual);
              }
          });
    
      public DrawingVisual DrawingVisual
      {
        get { return _drawingVisual; }
        set
        {
          if(DrawExtra) RemoveVisualChild(_drawingVisual);
          _drawingVisual = value;
          if(DrawExtra) AddVisualChild(_drawingVisual);
        }
      }
      private DrawingVisual _drawingVisual;
    
    ...
    
      protected override int VisualChildrenCount
      {
        get { return base.VisualChildrenCount + (DrawExtra ? 1 : 0); }
      }
    
      protected override Visual GetVisualChild(int index)
      {
        return index==base.VisualChildrenCount ? DrawingVisual : base.GetVisualChild(index);
      }