views:

108

answers:

3

Hey, I'm working a lot with Visual Studio 2008, .NET C# 2.0-3.5 and Windows Forms and I have noticed, like many before me, that GDI+ is extremely slow in drawing Controls. Note that I do not deal with images (JPG, GIF etc) very much. Images are only as icons in certain places. This is actually Controls/Forms/etc that are slow to draw.

The issue is that you can see Controls being drawn and it can take several seconds for a seemingly easy set of Controls to be drawn. Ie, its lagging and horrible.

I have made tests where I just put a number of Labels (40-50) on a form, hitting F5 to run and have to wait for them to be drawn. Again, lag and not a very nice experience.

So, then there is WPF that might address this problem, but I/we are not ready to move to WPF. So I'm looking around for workarounds or fixes and I stumbled upon Direct2D, and when reading on that some other libraries.

Put Im a tad puzzled and thus these questions:

1) First of, what I want is a fairly neat and simple way to just replace GDI+ with something faster and hardware accelerated approach. Is it possible to do that without going over to WPF and without having to rewrite all my Windows Forms code?

Whenever I read anything on Direct2D I see long blocks of usually horrible C++ code, telling me on how to manually write code to for drawing. I do not want that.

2) While reading on the net, I stumbled upon SlimDX, but I cannot figure out how to use it (and I admit, I havent tried very much as of writing). Lets say I already have a GUI-application (Windows Forms, standard C# code) - can I somehow use SlimDX (or something like it) to just "replace" GDI+ without too much rewriting?

My problem is I cannot find any examples or such telling me if it is possible to use SlimDX, Direct2D or other similiar things in my already-created Windows Forms software, and if it is possible - how to do it.

Hope Im not too fuzzy =)

==EDIT== 2010-09-22

I have made some tests in my real app, and isolated one of the slow things to this:

When I add text to some Labels in a UserControl, the Controls resize themselves to fit the text. For example, the containing GroupControl adapts a bit to the size of the text that was just added to the .Text-property of the Labels.

There are about 10 Label controls. The first time the labels are updated, and thus sizes are changed, the whole process takes about 500 ms. The second time the labels are updated, and no size changes, it takes about 0 ms.

==EDIT 2== 2010-09-22

Found one of the slow-downs. Then adding a String to the Text-property it is slow if the text that is being added differs in string length from the text that was there before the update.

Im using DevExpress libraries, and the LabelControls can be set to AutoSizeMode. If I set that to "None" then the lag will go away when adding text that differs in length from the previous text. I guess this problem will be the same for the normal Label-control as it also has a AutoSize = true/false setting.

However, its a "workaround" but still proves my point - its really slow when resizing which is pretty lame.

A: 

On your first question, I had to use GDI to do some image processing stuff which was taking ages under GDI+. This was 4-5 years ago and working with GDI using managed C# was a pain - not sure how much it has changed now. There are many good and fast functions such as BitBlt which are very fast in drawing but you need to be very careful with releasing resources (handles) and memory. I also had another issue and that was saving the result as JPEG and it is non-existent in GDI so I had to use CxImage to read the HBitmap and then save it.

All in all, GDI is very fast and robust. If there better abstractions in DirectX, probably you are better off using them.

Aliostad
Thx. Well, I dont deal with pictures very much. Its the drawing of normal Windows Forms controls, bringin up Forms and suchs that is slow. GDI is fast? I read everywhere that it is not?
Ted
A: 

We use SlimDX in our C# app.... But we're actually doing 3D. We wrote our own 2D lib to be able to do simple 2D drawing. SlimDX is just a lightweight wrapper around DirectX. So you'll get all of the pro's and cons of DirectX. Like that it's your problem to emulate the videocard if its not present.

If you want something for drawing to offscreen bitmaps, I'd go for WPF, as it is well integrated with C#, works mostly everywhere, and accellerated when there's hardware available. You can copy the output to a bitmap and use that in regular GDI/Winforms. But it will only be faster than GDI+ if you do fairly complex stuff (lots of filters, mixing textures etc...).

Edit:

In response to the comments, I built a little sample form. There is a seconds long wait when switching the first time, but after that it's responsive. Slower than I'd expect, but by all means usable. Would like Ted to comment if this is about the performance he is seeing in his app.

public class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        FillTab(tabPage1, Color.White);
        FillTab(tabPage2, Color.Yellow);

    }

    private static void FillTab(TabPage tabPage, Color color)
    {
        for (int i = 0; i < 200; ++ i)
        {
            int left = (i % 5) * 320;
            int topOffset = (i / 5) * 23;
            tabPage.Controls.Add(new Label { Left = left, Top = topOffset, Width = 100, Text = "Label" });
            tabPage.Controls.Add(new TextBox() { BackColor = color, Left = left + 100, Top = topOffset, Width = 100, Text = tabPage.Text });
            tabPage.Controls.Add(new ComboBox { BackColor = color, Left = left + 200, Top = topOffset, Width = 100 });
        }

    }

    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    #region Windows Form Designer generated code

    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
        this.tabControl1 = new System.Windows.Forms.TabControl();
        this.tabPage1 = new System.Windows.Forms.TabPage();
        this.tabPage2 = new System.Windows.Forms.TabPage();
        this.tabControl1.SuspendLayout();
        this.SuspendLayout();
        // 
        // tabControl1
        // 
        this.tabControl1.Controls.Add(this.tabPage1);
        this.tabControl1.Controls.Add(this.tabPage2);
        this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill;
        this.tabControl1.Location = new System.Drawing.Point(0, 0);
        this.tabControl1.Name = "tabControl1";
        this.tabControl1.SelectedIndex = 0;
        this.tabControl1.Size = new System.Drawing.Size(292, 266);
        this.tabControl1.TabIndex = 0;
        // 
        // tabPage1
        // 
        this.tabPage1.Location = new System.Drawing.Point(4, 22);
        this.tabPage1.Name = "tabPage1";
        this.tabPage1.Padding = new System.Windows.Forms.Padding(3);
        this.tabPage1.Size = new System.Drawing.Size(284, 240);
        this.tabPage1.TabIndex = 0;
        this.tabPage1.Text = "tabPage1";
        this.tabPage1.UseVisualStyleBackColor = true;
        // 
        // tabPage2
        // 
        this.tabPage2.Location = new System.Drawing.Point(4, 22);
        this.tabPage2.Name = "tabPage2";
        this.tabPage2.Padding = new System.Windows.Forms.Padding(3);
        this.tabPage2.Size = new System.Drawing.Size(245, 217);
        this.tabPage2.TabIndex = 1;
        this.tabPage2.Text = "tabPage2";
        this.tabPage2.UseVisualStyleBackColor = true;
        // 
        // Form1
        // 
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(292, 266);
        this.Controls.Add(this.tabControl1);
        this.Name = "Form1";
        this.Text = "Form1";
        this.tabControl1.ResumeLayout(false);
        this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.TabControl tabControl1;
    private System.Windows.Forms.TabPage tabPage1;
    private System.Windows.Forms.TabPage tabPage2;
}
jdv
I dont think Im following. offscreen bitmaps? ehm, I just want the controls on my UserControls/forms to be drawn quickly and without the lag. Are u saying that GDI+ is as fast is it gets? Cause its really slow. I just tried adding about 160 TextBoxes otno an empty Form, and you can the the lag when redrawing that area...
Ted
@Ted: In that case WPF is maybe not appropriate. Could you post some code showing what you do exaclty with those controls? 160 empty textboxes, that is 160 rectangles, should be fast in any technology.
jdv
Hey, WPF is as I said not an option since it would require a lot of rewrite and thats not an option. The code is simple a new Windows Forms project, 1 TabControl that contains about 160-180 TextBoxes. On the next TabPage there is about 200-300 LabelControls and on the next TabPage about 200 ComboBoxes. If you then change TabPage you can see the effect clearly. thx.
Ted
200 textboxes/comboboxes?! Are you sure you're not better off using a datagrid-type control or something? Because I question the value of having so many controls on one form.
Alex Paven
@Alex Thats not the point. Its just an example to show how slow it is. That was just a test project. In my normal apps we use a lot of different controls, and its is SO SLOW that it makes me wanna cry.
Ted
Any technology is slow if you use it inappropriately. Neither WinForms nor WPF were designed for that kind of brutality. You must aggregate your controls somehow, make use of virtualization, and other similar techniques to take it down to manageable size. Of course all these frameworks bring some overhead with them, so it's unrealistic to expect them to be fast under such circumstances. Also, at one of your previous comments - a textbox is NOT a rectangle. It provides so much functionality I can't even fit in in this comment. Think undo/redo, multi-language, scrolling etc etc etc
Alex Paven
So, what you are saying that simple Labels cannot be in a form without them being slow. This illustrated my point with an extremely slow Framework. The textbox is a very simple control. Also see my update in my question above.
Ted
@Alex: Ted's question is about replacing the GDI painting of controls with another techology. So my point that a textbox (an empty one) is no more work than a rectangle, referred to the GDI involvement, the painting. There is for sure lots of overhead to be found here somewhere, but I doubt if GDI is the bottleneck.
jdv
@Ted: Check the sample code I added if you like.
jdv
@jdv - Indeed, the actual drawing code is almost definitely not the bottleneck. One can easily test that by drawing the same things the textbox draws - I think there's a helper class somewhere in the framework that does exactly that. Draw that 200 times and I don't thing you'd have a problem regardless of the technology. Controls on the other hand are responsible for a zillion other things, most importantly layout - each control has alignments, dock settings etc etc.
Alex Paven
@jdv Thanks, I will take a look at it now!
Ted
I updated with an Edit 2, explaining the problem I found. The problem is with the framework that is insanely slow when "resizing" (or whatever it does). It should be that way.
Ted
@jdv I ran you example code, and it most definately lags. When switching between the pages it takes about.... 150-200 ms? Something like that for the page to draw...
Ted
@Ted: If you read my edit carefully, I did not claim it was instantaneous. Given that you changed the question, is painting still an issue for you?
jdv
@jdv Yes, the painting is still an issue even though it is a lot better when skipping half a second of wait time... =)
Ted
@Ted: Pity. Are you talking about my sample, your sample (which I haven't seen) or your production code? And how many labels/controls you have on each page in your production code? I still have the idea I'm missing something.
jdv
@jdv I tried your example, and I can see the "lag" when it is showing the Controls. In my example there is around 200 labels/comboboxes per TabPage... You dont see that?
Ted
@Ted: Yes, I do see some lag (and I think I already confirmed that). It's along the lines of the 200ms you mentioned. But that's an example with 3 times more controls than what you say you need. Why haven't modified the sample such that it is about as complex as what you actually need? When I reduce the control count in my sample to 200, there is no noticable lag. So I fail to see what the problem is.
jdv
A: 

You could try managed directx, but they no longer support it (moved on to XNA). Honestly, unless you've got a shitty computer or a ton of controls, I don't know why it'd be lagging so bad. If you're doing some cpu intensive stuff on your main thread, move it to a separate thread. That's the only other reason I can think of that'd cause that kind of lag.

Phil Winkel
I have a Intel 920 2.6 Ghz with 6 GB ram, running om Win7 64bit with LOADS of RAM available and a bored CPU. No, its not the computer =) A testproject of just addding a bunch of Labels, TextBoxes in a TabControl shows the problem. It lags very visibly when switching tabs (and the tabPages contain hunders of Labels or TextBoxes or whatever)....
Ted