0

I have a component, an inherited Panel, where I am overriding the OnPaint event to draw a graph with 500 points. Since I need to do some selection on the graph, it is flickering. I have found this DoubleBuffered property but when I set it to True, in the panel constructor, the drawing disappears. I debug it and I see that the drawing methods still execute but there is nothing on the panel. Does anyone know why would this happen?

This is .NET 3.5 - C#. Winforms application

        try
        {
            Graphics g = e.Graphics;

            //Draw _graphArea:
            g.DrawRectangle(Pens.Black, _graphArea);

            _drawingObjectList.DrawGraph(g, _mainLinePen, _diffLinePen, _dotPen, _dotBrush, _notSelectedBrush, _selectedBrush);

            DrawSelectionRectangle(g);

            g.Dispose();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }

Panel descendant constructor:

        this.BackColor = Color.White;
        this.SetStyle(ControlStyles.ResizeRedraw, true);
        this.SetStyle(ControlStyles.UserPaint, true);
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        this.UpdateStyles(); 
Cœur
  • 37,241
  • 25
  • 195
  • 267
elector
  • 1,327
  • 4
  • 26
  • 43
  • I assume this is .Net? language? – rene Mar 01 '11 at 13:32
  • @rene Yes, it is. Sorry. C#. It's in the question now. – elector Mar 01 '11 at 13:55
  • Can you show us your drawing code? – FreeAsInBeer Mar 01 '11 at 13:59
  • Is changing the DoubleBuffered property to true the _only_ thing that changed? – bentsai Mar 01 '11 at 14:08
  • 2
    Yes, the code would be helpful. It sounds like the default background is overwriting your image. You might not have set UserPaint, and AllPaintingInWMPaint class bits. – Ritch Melton Mar 01 '11 at 14:09
  • @FreeAsInBeer, @Rich Melton, I can't really post the code cause I have a generic list of graph segments that have a drawing method so the actual g.DrawLine is in them. But I'll try to enter the UserPaint and AllPainting... properties and let you know. – elector Mar 01 '11 at 14:31
  • @bentsai Yes. The only thing. Now I have added the UserPaint and AllPaintingInWMPaint and set them to true but still the same thing. – elector Mar 01 '11 at 14:52
  • Since I can't see your code, this is a guess. You are using e.Graphics that is passed to your method and not creating your own, right? That was [the issue](http://stackoverflow.com/questions/3113190/double-buffering-when-not-drawing-in-onpaint-why-doesnt-it-work) that plagued another user. – FreeAsInBeer Mar 01 '11 at 14:53
  • @FreeAsInBeer, @Rich Melton I have tried with UserPaint and AllPaintingInWMPaint and set them to true but still the same thing. Could this be something related to the fact that I have the drawing in the generic elements list? – elector Mar 01 '11 at 14:54
  • @FreeAsInBeer Yes, this is exactly what I am doing. I have: Graphics g = e.Graphics; and then I pass it to my objects DrawGraph method. In this method I am going through foreach loop where again I am passing the g to every element draw method. Should I perhaps use ref with the g parameter? – elector Mar 01 '11 at 15:00

1 Answers1

1

Try using ControlStyles.OptimizedDoubleBuffered instead. It's faster and usually works better. Make sure ControlStyles.AllPaintingInWmPaint and ControlStyles.UserPaint are also enabled.

Now, OnPaint() should be the only stuff that draw to the window and this method must only be called from invalidation or by using Refresh(); you must never call OnPaint() yourself. Do not dispose the Graphics object. If you fail any of those conditions, flickering and various other drawing bugs might occur.

class MyControl : UserControl
{
    public MyControl()
    {
        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.UserPaint, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    }

    protected override void  OnPaint(PaintEventArgs e)
    {
        e.Graphics.Clear(Color.Red);
    }

    void RandomEventThatRequiresRefresh(object sender, EventArgs e)
    {
        Refresh();
    }
}
Coincoin
  • 27,880
  • 7
  • 55
  • 76
  • I am doing all that. I use this.Invalidate() to refresh the graph. But, as you see in my code, I don't do the drawing in the OnPaint. I have other objects that do the drawing. – elector Mar 01 '11 at 16:46
  • 1
    @elector It's ok to draw from another method, as long as that method is called by `OnPaint` or a callee and that you are always using the `e.Graphics` object given to you by the event handler. See if removing the g.Dispose call helps. I didn't notice it the first time I saw the question. – Coincoin Mar 01 '11 at 16:54
  • YES! Removing the g.Dispose solved it! I don't know why that was there, I was following examples from C# Graphics book and this peace of code comes from there! Thank you – elector Mar 01 '11 at 20:44