WPF自定义控件绘图避免无限测量/排列循环

时间:2015-08-19 13:11:23

标签: c# wpf

我正在构建一个自定义控件,它根据某些数据执行一些自定义绘图。我想在调用排列时更新图形(即大小已更改)。但是当我在ArrangeOverride()中改变我的孩子时,我得到一个无限循环。我怎么能避免这个?

为简单起见,我更容易重建整个可视树而不是创建一次子项并单独调整它们的大小。

有更好的方法吗?我也可以使用DrawingContext对象并在那里调用我的绘图逻辑。

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

1 个答案:

答案 0 :(得分:0)

以下是我解决它的方法:

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