1

I am building a custom control that does some custom drawing based on some data. I want to update the drawing when arrange is called (i.e. the size is changed). But when I am changing my Children in ArrangeOverride() I get an infinite loop of course. How can I avoid this?

For simplicity it is easier for me to rebuild the whole visual tree instead of creating children once and resizing them individually.

Is there a better approach to do this? I can also live with just using a DrawingContext object and invoking my drawing logic there.

public class MyCanvas : Canvas
{
    private static int _drawCounter = 0;
    private System.Windows.Size _arrangeSize;
    private MyData _data;

    protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
    {
        _arrangeSize = arrangeSize;
        Draw();
        return base.ArrangeOverride(arrangeSize);
    }

    public void SetData(MyData data)
    {
        _data = data;
        Draw();
    }

    private void Draw()
    {
        Children.Clear();

        if (_data == null || _arrangeSize.IsEmpty)
        {
            return;
        }

        Children.Add(new TextBlock() {Text = (++_drawCounter).ToString()});
    }
}
metacircle
  • 2,438
  • 4
  • 25
  • 39
  • possible duplicate of [Force a custom WPF Control to resize correctly](http://stackoverflow.com/questions/518674/force-a-custom-wpf-control-to-resize-correctly) – netaholic Aug 19 '15 at 13:19
  • Don't call Draw in ArrangeOverride. Better override OnRenderSizeChanged. You should also avoid to reset the Children collection in each Draw call. Just rearrange the child elements. – Clemens Aug 19 '15 at 13:39

1 Answers1

0

Here is how I solved it:

public class MyCanvas : Canvas
{
private readonly DispatcherTimer _dispatcherTimer;
private Size _arrangeSize;
private Size _drawnSize;

public MyCanvas()
{
    _dispatcherTimer = new DispatcherTimer(DispatcherPriority.Render)
        {
            Interval = TimeSpan.FromMilliseconds(500)
        };
    _dispatcherTimer.Tick += (sender, args) =>
        {
            var dispatcherTimer = (DispatcherTimer)sender;
            dispatcherTimer.Stop();
            Debug.WriteLine("Draw call from DispatcherTimer");
            Draw();
        };
}

protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
     _arrangeSize = arrangeSize;
    if (_drawnSize != _arrangeSize)
    {
        QueueDrawCall();
    }
    return base.ArrangeOverride(arrangeSize);
}

private void QueueDrawCall()
    {
        if (_dispatcherTimer.IsEnabled)
        {
            _dispatcherTimer.Stop();
        }

        _dispatcherTimer.Start();
    }

public void SetData(MyData data)
{
    _data = data;
    Console.WriteLine("Direct Draw Call " + data);
    Draw();
}

private void Draw()
{
    if (Children.Count > 0)
    {
        Children.Clear();
    }

    if (_data == null || _arrangeSize.IsEmpty)
    {
        return;
    }

    InternalDraw(); // Drawing logic goes in this function

    _drawnSize = _arrangeSize;
}
}
metacircle
  • 2,438
  • 4
  • 25
  • 39