Click here to Skip to main content
15,881,882 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I am developing a Paint Program in c# WinForms.

Problem :
Screen is still flickering even after setting DoubleBufered to true.

Here is my code

C#
// Variables
 Bitmap pic;
 Graphics g;
 bool mouse_down = false;
 private void panel1_MouseDown(object sender, MouseEventArgs e)
 {
     mouse_down = true;
 }

 private void panel1_MouseUp(object sender, MouseEventArgs e)
 {
     mouse_down = false;
 }

 private void Form1_Load(object sender, EventArgs e)
 {
     pic = new Bitmap(panel1.Width, panel1.Height);
     g = Graphics.FromImage(pic);
 }

 private void panel1_MouseMove(object sender, MouseEventArgs e)
 {

     if (mouse_down)
     {
         panel1.Invalidate();
    g.FillRectangle(Brushes.Black,new Rectangle(e.X,e.Y,5,5));
     panel1.BackgroundImage = pic;
     }
 }


I know this is because of "panel1.Invalidate()". If i comment out this line of code then only one rectangle apprers on the panel and nothing is drawn in second attempt.

How do i fix the problem ?

Any kind of help would be appreciated.
Posted
Updated 23-Jan-18 20:02pm
v2
Comments
[no name] 11-Jan-15 10:56am    
What do you expect other than flickering? Think abot "Invalidating" on each mouse move event.
Sergey Alexandrovich Kryukov 11-Jan-15 12:07pm    
I cannot see where OP invalidates, that's the problem. And you are right.
There is a bit more to it, so I post an answer, please, see. I credited your advice.
—SA
[no name] 11-Jan-15 12:22pm    
Thank you very much for this. Even I think you did 99.9999% of the Job.
Bruno
BibhutiAlmighty 11-Jan-15 12:15pm    
I googled about the meaning of "OP" but couldn't get it . Whatdoes that mean ?
Sergey Alexandrovich Kryukov 11-Jan-15 12:36pm    
Oh, I'm sorry... I'm myself against the acronyms, but using this one because the usual CodeProject practice, so I adopted it even understanding it's confusing. I think it means "Original Poster" (original post author), in this case, you. :-)
Sorry for the confusion.
—SA

Stop all that Invalidate - it is the cause of the flickering. You are repainting the whole panel with an extreme frequency...
Your second problem is the way you try to handle you Graphics object...You create an image and from that a Graphics object and then assign it back to the panel as background...This is a total waste IMHO...
Try drawing directly to the Graphics object of the panel!!!
C#
public partial class Form1 : Form
{
    Graphics g;
    bool mouse_down = false;

    public Form1 ( )
    {
        InitializeComponent ( );
    }

    private void panel1_MouseDown ( object sender, MouseEventArgs e )
    {
        mouse_down = true;
    }

    private void panel1_MouseMove ( object sender, MouseEventArgs e )
    {
        if ( mouse_down )
        {
            g.FillRectangle ( Brushes.Black, new Rectangle ( e.X, e.Y, 5, 5 ) );
        }
    }

    private void panel1_MouseUp ( object sender, MouseEventArgs e )
    {
        mouse_down = false;
    }

    private void Form1_Load ( object sender, EventArgs e )
    {
        g = panel1.CreateGraphics ( );
    }
}


[EDIT]
As Sergey properly stated, my solution is very clumsy in a way that it's only solves the immediate problem of the presented code, but do not addresses the real problem - persistent drawing!
To explain the problem here a very short explanation of how painting in Windows works:
Your window (panel or any other control that part of it) is drawing itself - means it is not Windows the OS who paint those nice borders and buttons on your window but the window itself (and its parts) has the knowledge how to be painted...
But! The when (and what part) is synchronized by Windows via sending paint message (WM_PAINT) to each and every window according to changes in it state and/or visibility. For instance a window became visible (by closing the window on top of it) and Windows send it a paint message to notify it about the event.
Now, your window (the panel) has a lot of painting on it that was created from the mouse events, but what will happen if some other window overlaps (hides) a part of your window? That hidden part will be lost and when you window became visible a second time - and receive a paint message - no-one will know how to re-create it...
There should be two main methods to persists the knowledge of how to re-create the drawing:
1. Record all the paintings you made in a kind of queue and upon paint message use that queue to draw all the paintings again. It can be very useful for a more sophisticated paint program that can handle every painted 'object' (like circle, rectangle or line) individually...
2. Create a shadow copy - disconnected from the main window - of your painting and copy it back upon paint message

I will show a sample for the second approach (as it's much more sample) now:
C#
public partial class Form1 : Form
{
    Graphics g;
    Graphics g_shadow;
    Bitmap bmp;
    bool mouse_down = false;

    public Form1 ( )
    {
        InitializeComponent ( );
    }

    private void panel1_MouseDown ( object sender, MouseEventArgs e )
    {
        mouse_down = true;
    }

    private void panel1_MouseMove ( object sender, MouseEventArgs e )
    {
        if ( mouse_down )
        {
            // draw both to the panel and to the shadow image
            g.FillRectangle ( Brushes.Black, new Rectangle ( e.X, e.Y, 5, 5 ) );
            g_shadow.FillRectangle ( Brushes.Black, new Rectangle ( e.X, e.Y, 5, 5 ) );
        }
    }

    private void panel1_MouseUp ( object sender, MouseEventArgs e )
    {
        mouse_down = false;
    }

    private void Form1_Load ( object sender, EventArgs e )
    {
        g = panel1.CreateGraphics ( );
        // here create a second Graphics object that will paint to an non-visible shadow image
        bmp = new Bitmap ( panel1.Width, panel1.Height );
        g_shadow = Graphics.FromImage ( bmp );
    }

    private void panel1_Paint ( object sender, PaintEventArgs e )
    {
        // copy the shadow image to the main panel
        e.Graphics.DrawImageUnscaled ( bmp, e.ClipRectangle );
    }
}
 
Share this answer
 
v2
Comments
Sergey Alexandrovich Kryukov 11-Jan-15 12:42pm    
I'm sorry, this is just the wrong solution. It won't preserve the results of those strokes, because you don't use OnPaint. If you use it, you should have specified this important moment.

In fact, Invalidate is not the source of flickering if you use it correctly. One can invalidate only the part of the scene. Now, what about CreateGraphics? Yes, you can use it, but in much more sophisticated way. One way is: you can use both instances of Graphics: one you create, and another passed in OnPaint. First used for drawing "here and now", another one for rendering after invalidation. This is just the idea. But! simply drawing it all in OnPaint only will work, maybe with lower performance.

Did you try to create a drawing application yourself? From the first glance, what you described won't work. It may work if you add a lot more detail, but you didn't.

Thank you for understanding.

—SA
Kornfeld Eliyahu Peter 11-Jan-15 12:49pm    
I didn't meant to create a paint application to OP only point out the main problems I saw.
You right about the 'non-preserving' part. This solution will not re-paint old part (let say overlapped by other window), but only from now-on...However OP's original didn't done it too...
You also true about the that Invalidate by itself won't be a problem, but in the way OP uses it IS...
Sergey Alexandrovich Kryukov 11-Jan-15 12:59pm    
I understand, but you need to do something about it. If one directly follows your code, it won't work out.
If you understand how to fix it the way it would be applicable to real drawing application, you just have to do it.
—SA
Kornfeld Eliyahu Peter 11-Jan-15 16:11pm    
A small update - I took this code and created a project, and surprise-surprise...It's working. It's working in the meaning of preservation of drawing. Despite what you (and to be honest me too :-)) were thinking the panel inside the form preserves the drawings across resize, overlap and moving...
My last GUI project was written in C++ using plain API calls and from that this is somehow unexpected...I will have to see it in details...
Sergey Alexandrovich Kryukov 11-Jan-15 16:17pm    
Please understand me right. If this is a surprise to you, you should not post it until you understand how it works in all detail. And then you should provide all information, not just what you wrote. As written, it won't work; it should be something else. Please tell me: do you use OnPaint? handle Paint event.

If not, I'm not sure it preserves drawing. Do this: move the window to left or to right, behind the screen, to cover the place where the strokes were. Then move it back to make the strokes visible. Are they preserved? But if they are, you did not provide essential part of code. You really need to put those stokes in some data, preserve this data, and use this data for rendering on invalidation. By no invalidating, you conceal the problem, but don't solve it.

—SA
I don't see where you invalidate what. You need to invalidate the was Bruno Sprecher advised in his comment.

Just one advise, which along may not be a solution, but should work together with your whole design: you don't have to invalidate the whole panel. When the user does the painting stroke, calculate its extents and invalidate only the part of control where you paint. Please see two other System.Windows.Forms.Conrol.Invalidate methods, those accepting a Rectangle or Region arguments: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invalidate%28v=vs.110%29.aspx[^].

[EDIT]

I forgot to comment on one more questionable piece in your code, using Graphics.FromImage. As a result, I don't know how you solution really works. You are drawing on a bitmap, not on screen. It's possible that rendering on screen is done in some other piece of the of the code you don't show. And there could be the real problem.

You see, rendering in two steps, first on an image and then the image on screen, could be done well or purely. But if you do it well, it would be just redundant double-buffering. Also, you can do it in a wrong way, then you could flicked on the area of this bitmap. So, I would advise not to use rendering of the bitmap at all and rely on double buffering.

Please see my past answers explaining proper rendering and invalidation, with some background in the first answer:
What kind of playful method is Paint? (DataGridViewImageCell.Paint(...))[^],
capture the drawing on a panel[^],
Drawing Lines between mdi child forms[^].

In your case, here is the simplest flicker-free approach: render on some control with overridden OnPaint. This part should ignore strokes; instead, have some data model and render model data on the control. Don't worry about redundant rendering and performance: WM_PAINT works in some cunning manner to optimize things. Your strokes should write not to screen, but too data; after the stroke, you should invalidate that small part of the control re-touched by the stroke. Be careful to cover all areas which should change.

In more sophisticated approach, draw immediately on screen and data at the same time. Drawing on the screen will be overwritten by rendering on invalidation. Be careful to ensure both phase of drawing do the same.

—SA
 
Share this answer
 
v5
Comments
BillWoodruff 11-Jan-15 17:03pm    
+5 partly to counter a stupid down-vote, and mainly to say I think this advice is sound.
Sergey Alexandrovich Kryukov 11-Jan-15 18:17pm    
Thank you, Bill.
—SA
Mohamed Mitwalli 12-Jan-15 0:46am    
5+
Sergey Alexandrovich Kryukov 12-Jan-15 1:11am    
Thank you, Mohamed.
—SA
You need to add
C#
Invalidate();
in
C#
form_resize event.
that's it.

I have solved the problem in my winform application by adding this in it.
 
Share this answer
 
Comments
CHill60 24-Jan-18 4:03am    
Three years too late and you clearly did not read the other solutions and comments!
Stick to answering new posts where the OP still needs help
Santosh Kokatnur 24-Jan-18 4:08am    
Well, I just replied to it. Because I was also facing the same problem before. So that I have answered.

Thank you!

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900