DrawingVisual не обновляется

Я создаю свой собственный FrameworkElement и переопределяю VisualChildrenCount{get;} и GetVisualChild(int index), возвращая свой собственный экземпляр DrawingVisual.

Если я изменяю содержимое визуального элемента после первоначального рендеринга (например, в обработчике таймера), используя DrawingVisual.RenderOpen() и рисуя в контексте, элемент не обновляется.

Вот самый простой образец:

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;

namespace VisualTest
{
    public class TestControl : FrameworkElement
    {
        private readonly DrawingVisual _visual = new DrawingVisual();

        public TestControl()
        {
            Draw(false);

            var timer = new DispatcherTimer {Interval = new TimeSpan(0, 0, 2)};
            timer.Tick += (sender, args) =>
                              {
                                  Draw(true);
                                  InvalidateVisual();
                                  timer.Stop();
                              };
            timer.Start();
        }

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

        protected override int VisualChildrenCount
        {
            get { return 1; }
        }

        private void Draw(bool second)
        {
            DrawingContext ctx = _visual.RenderOpen();
            if (!second)
                ctx.DrawRoundedRectangle(Brushes.Green, null, new Rect(0, 0, 200, 200), 20, 20);
            else
                ctx.DrawEllipse(Brushes.Red, null, new Point(100, 100), 100, 100);
            ctx.Close();
        }
    }
}

InvalidateVisual() ничего не делает. Хотя, если вы измените размер окна, содержащего элемент, оно обновится.

Любые идеи о том, как правильно обновить содержимое? Желательно без введения новых свойств зависимостей для моего элемента.


person repka    schedule 02.11.2009    source источник


Ответы (3)


Добавлять

this.AddVisualChild(_visual);
this.AddLogicalChild(_visual);

в конструктор класса TestControl.

person SMART_n    schedule 02.11.2009
comment
Этот код приведет к утечке памяти визуального элемента, поскольку он добавляется в дерево, но никогда не удаляется. Лучший код — добавить визуализацию в событие «Загружено» и удалить ее в событии «Выгружено» — см. мой ответ. - person splintor; 17.06.2012

На основе ответа SMART_n приведено улучшенное решение, не допускающее утечки памяти:

    public TestControl()
    {
        Loaded += AddVisualToTree;
        Unloaded += RemoveVisualFromTree;

        Draw(false);

        var timer = new DispatcherTimer {Interval = new TimeSpan(0, 0, 2)};
        timer.Tick += (sender, args) =>
                          {
                              Draw(true);
                              InvalidateVisual();
                              timer.Stop();
                          };
        timer.Start();

    }

    private void AddVisualToTree(object sender, RoutedEventArgs e)
    {
        AddVisualChild(_visual);
        AddLogicalChild(_visual);
    }

    private void RemoveVisualFromTree(object sender, RoutedEventArgs e)
    {
        RemoveLogicalChild(_visual);
        RemoveVisualChild(_visual);
    }
person splintor    schedule 17.06.2012
comment
Это работает отлично. Обратите внимание, что Load и Unloaded могут возникать несколько раз на протяжении всего жизненного цикла элемента фреймворка — я предположил, что они были вызваны только один раз, что привело к тому, что мой визуальный элемент не перерисовывался после нескольких часов бездействия. - person Cameron; 23.07.2020

Если вы сделаете _visual DrawingGroup, вы сможете повторно открыть его позже и изменить его команды рисования, и они будут обновлены.

person David Jeske    schedule 10.06.2017