加快在WPF中向Canvas添加对象的速度

时间:2014-06-01 02:09:07

标签: c# wpf optimization canvas

我在WPF中使用了Canvas来绘制许多彩色矩形,但程序在添加时运行速度非常慢。我尝试了不同的选项,例如将它们添加到Array并一次添加它们并使用Image代替Canvas来显示它们,但它们似乎没有做太多。我有一个代码在一个线程中引导绘图,但由于C#规则,我必须在主线程中有绘图部分。我还应该注意问题不是我的电脑(它运行的英特尔酷睿i7配备14GB DDR2内存)。

这是添加矩形的代码。它跑了83,000次。

    private void AddBlock(double left, double top, double width, double height, Brush color)
    {
        if (this.Dispatcher.Thread != Thread.CurrentThread)
        {
            this.Dispatcher.Invoke(new Action<double, double, double, double, Brush>(this.AddBlock), left, top, width, height, color);
            return;
        }

        Rectangle rect = new Rectangle() { Width = width, Height = height, Fill = color, SnapsToDevicePixels = true };

        this.canvas.Children.Add(rect);

        Canvas.SetLeft(rect, left);
        Canvas.SetTop(rect, top);
    }

注意:正如我在下面的评论中所述,我想要一些允许它在单独的线程上运行的东西(即使它涉及使用P / Invoke),因为似乎没有一个可行的解决方案,只使用C#和WPF。

有什么建议吗?

1 个答案:

答案 0 :(得分:4)

使用OnRender方法

我创建了一个继承Canvas的类,并重写OnRender方法以获取DrawingContext并使用它来绘制。所以在代码中我不添加rects到画布,而是添加到新类的rect列表中,并在完成add后使用Dispatcher调用InvalidateVisual();

class MyCanvas:Canvas
{
    public class MyRect
    {
        public Rect Rect;
        public Brush Brush;
    }

    public List<MyRect> rects = new List<MyRect>();

    protected override void OnRender(System.Windows.Media.DrawingContext dc)
    {
        base.OnRender(dc);
        for (int i = 0; i < rects.Count; i++)
        {
            MyRect mRect = rects[i];
            dc.DrawRectangle(mRect.Brush, null, mRect.Rect);
        }
    }
}

XAML

<l:MyCanvas x:Name="canvas"/>

添加rects

private void AddBlock(double left, double top, double width, double height, Brush color)
{
    canvas.rects.Add(new MyCanvas.MyRect() { Brush = color, Rect = new Rect(left, top, width, height) });
}

准备好后刷新,应该在调度员上进行

canvas.InvalidateVisual();

这似乎是在WPF中绘制的最快方式,你可能不需要去GDI +或pinvoke。在我的系统测试过程中,原始代码花了大约500 ms来渲染830 rects,几何体花了大约400毫秒来渲染相同的内容,这种方法在83,000 rects中呈现100 ms少于GeometryGroup gGroup;

另外我建议你添加一些缓存以避免过度渲染

使用几何的解决方案

班级变量

DrawingBrush dBrush= new DrawingBrush();
gGroup = new GeometryGroup();
GeometryDrawing gDrawing = new GeometryDrawing(Brushes.Red, null, gGroup);
dBrush.Drawing = gDrawing;
Canvas.Background = dBrush

使用以下代码准备

private void AddBlock(double left, double top, double width, double height, Brush color)
{
    if (this.Dispatcher.Thread != Thread.CurrentThread)
    {
        this.Dispatcher.Invoke(new Action<double, double, double, double, Brush>(this.AddBlock), left, top, width, height, color);
        return;
    }
    //color need to figure out as it is added in GeometryDrawing 
    //currently Brushes.Red defined earlier
    gGroup.Children.Add(new RectangleGeometry(new Rect(left, top, width, height)));
}

然后是你的代码

{{1}}

此示例可帮助您实现相同目标。我也会尽快做一些实验,以更快的速度获得你想要的结果。