repka
repka

Reputation: 2979

DrawingVisual is not refreshed

I create my own FrameworkElement and override VisualChildrenCount{get;} and GetVisualChild(int index) by returning my own DrawingVisual instance.

If I modify the content of the visual after initial rendering (e.g. in timer handler) using DrawingVisual.RenderOpen() and drawing into the context, the element is not refreshed.

Here's the simplest sample:

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() does nothing. Although if you resize the window containing the element, it gets updated.

Any ideas on how to properly refresh the content? Preferably without introducing new dependency properties for my element.

Upvotes: 8

Views: 3398

Answers (3)

David Jeske
David Jeske

Reputation: 2466

If you make _visual a DrawingGroup, you can re-open it later and change it's drawing commands, and they will be updated.

Upvotes: 0

splintor
splintor

Reputation: 10154

Based on SMART_n's answer, here is an improved solution that doesn't leak memory:

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

Upvotes: 7

SMART_n
SMART_n

Reputation: 1080

Add

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

to TestControl class constructor.

Upvotes: 9

Related Questions