Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
597 views
in Technique[技术] by (71.8m points)

c# - Speed up adding objects to Canvas In WPF

I have a Canvas that I am using in WPF to draw many colored rectangles to but the program is running really slow when they are being added. I have tried different options, such as adding them to an Array and adding them all at once and using a Image instead of a Canvas to dispay them, but they didn't seem to do much. I have the coding leading up the drawing in a thread, but because of C# rules, I have to have the drawing part in the main thread. I should also note that the problem isn't my computer (its running Intel Core i7 with 14GB DDR2 RAM).

This is the code that adds the rectangles. It is ran over 83,000 times.

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

NOTE: As I stated in a comment below, I would like something that allows it to run on a seperate thread (even if it involves working with P/Invoke) as there doesn't seem to a viable solution to just using C# and WPF.

Any suggestions?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Using OnRender method

I created a class inheriting Canvas, and override the OnRender method to get the DrawingContext and used it to draw. so in the code I dont add rects to the canvas but to the rect list in new class and call InvalidateVisual(); using Dispatcher once I am done with add.

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"/>

to add the 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) });
}

refresh when ready, should be made on dispatcher

canvas.InvalidateVisual();

This seems to be the fastest way to draw in WPF, you may not need to go till GDI+ or pinvoke. During tests in my system original code took approx 500 ms to render 830 rects and geometry took approx 400 ms to render the same, where as this approach rendered 83,000 rects in less then 100 ms

Also I would advice you to add some caching to avoid rendering excessively

A solution using geometry

class level variables

GeometryGroup gGroup;

prepare using the following code

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

then comes your code

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

This sample may help you achieve the same. I'll also do some experiment soon to get your desired result in a much faster way.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...