WPF: рисование поверх текстового блока - PullRequest
1 голос
/ 24 июня 2010

Я хочу иметь возможность рисовать на вершине 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 Ответ

2 голосов
/ 24 июня 2010

Ваши проблемы:

  1. GetVisualChild () должен возвращать base.GetVisualChild (index), кроме случаев, когда index == base.VisualChildrenCount.
  2. Вы забыли вызвать AddVisualChild (), когда DrawingExtra становится истинным или DrawingVisual изменяется
  3. Вы забыли вызвать RemoveVisualChild (), когда DrawingExtra становится ложным или DrawingVisual изменяется

Вы можете исправить # 2 и # 3, установив PropertyChangedCallback на DrawingExtra и добавив код в установщик DrawingVisual.

Объяснение: Это вызов 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);
  }
...