c# - 加快在 WPF 中将对象添加到 Canvas

标签 c# wpf optimization canvas

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

这是添加矩形的代码。它运行了超过 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 的可行解决方案。

有什么建议吗?

最佳答案

使用 OnRender 方法

我创建了一个继承Canvas的类,重写了OnRender方法得到了DrawingContext并用它来绘制。所以在代码中,我没有将矩形添加到 Canvas ,而是添加到新类中的矩形列表,并在完成添加后使用 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"/>

添加矩形

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 毫秒 来渲染 830 个矩形,而几何体用了大约 400 毫秒来渲染同样的东西,而这种方法渲染了 83,000 个矩形 不到 100 毫秒

此外,我建议您添加一些缓存以避免过度渲染

使用几何的解决方案

类级变量

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

此示例可能会帮助您实现同样的目标。我还将很快进行一些实验,以更快地获得您想要的结果。

关于c# - 加快在 WPF 中将对象添加到 Canvas,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23976163/

相关文章:

c# - 是否首先调用母版页?

c# - MVVM 卡住事件 Hook

c# - 如何在 Windows Phone 中使用必应搜索 API?

c# - 是否有可能以 mvvm 模式在 wpf datagrid 上获取动态列?

spring - 为什么 Spring 消息转换器仍然在 @Cachable 方法中被调用

c++ - 删除字符串中具有相同值的任何相邻字母的 “pair”

java - C# 相当于 Javas Guavas Maps.uniqueIndex

wpf - 在WPF Richtextbox中获取TextPointer的XY坐标

c# - 动态数据显示 : Change X-Axis date time format for graph

optimization - 是否有用于在简单处理器上生成自修改代码的高级语言的现代编译器?