WM_NCPAINT and Massive Amount of Flicker
-
Hello again, CP! I am now trying to create a custom Form by painting a new title bar. I have never done this before but I did remember seeing it a lot when I was heavy into C++ and MFC. So I knew to look for WM_NCPAINT. The resources I found were telling me to override WndProc for the form and catch WM_NCPAINT, and then do my custom drawing from there. So this is what I did:
protected override void WndProc(ref Message m)
{
// Handle non-client painting
IntPtr hdc = GetWindowDC(m.HWnd);
Graphics g = null;
if ((int)hdc != 0)
g = Graphics.FromHdc(hdc);if (m.Msg == WM\_NCPAINT || m.Msg == WM\_NCACTIVATE || m.Msg == WM\_SETTEXT) { if ((int)hdc != 0) { OnNcPaint(g); ReleaseDC(m.HWnd, hdc); m.Result = (IntPtr)1; } } else base.WndProc(ref m); }
In OnNcPaint(Graphics) I make a simple call to graphics.FillRectangle, which fills the entire rectangle with a solid color (in this case, Color.Blue). However, when I resize the form I am seeing a large amount of flickering across the entire form. The background is not custom painted. I have ResizeRedraw set in the form's "SetStyle()" method. When I remove ResizeRedraw it does not paint properly at all. How can I eliminate the flicker here? This is kind of irritating. Haha. :) BTW, I have "base.WndProc(ref m);" in the "else" statement because otherwise, if I call it alongside WM_NCPAINT, it will just paint Windows' non-client area (Win7, Aero Theme). Thanks in advance.
djj55: Nice but may have a permission problem Pete O'Hanlon: He has my permission to run it.
-
Hello again, CP! I am now trying to create a custom Form by painting a new title bar. I have never done this before but I did remember seeing it a lot when I was heavy into C++ and MFC. So I knew to look for WM_NCPAINT. The resources I found were telling me to override WndProc for the form and catch WM_NCPAINT, and then do my custom drawing from there. So this is what I did:
protected override void WndProc(ref Message m)
{
// Handle non-client painting
IntPtr hdc = GetWindowDC(m.HWnd);
Graphics g = null;
if ((int)hdc != 0)
g = Graphics.FromHdc(hdc);if (m.Msg == WM\_NCPAINT || m.Msg == WM\_NCACTIVATE || m.Msg == WM\_SETTEXT) { if ((int)hdc != 0) { OnNcPaint(g); ReleaseDC(m.HWnd, hdc); m.Result = (IntPtr)1; } } else base.WndProc(ref m); }
In OnNcPaint(Graphics) I make a simple call to graphics.FillRectangle, which fills the entire rectangle with a solid color (in this case, Color.Blue). However, when I resize the form I am seeing a large amount of flickering across the entire form. The background is not custom painted. I have ResizeRedraw set in the form's "SetStyle()" method. When I remove ResizeRedraw it does not paint properly at all. How can I eliminate the flicker here? This is kind of irritating. Haha. :) BTW, I have "base.WndProc(ref m);" in the "else" statement because otherwise, if I call it alongside WM_NCPAINT, it will just paint Windows' non-client area (Win7, Aero Theme). Thanks in advance.
djj55: Nice but may have a permission problem Pete O'Hanlon: He has my permission to run it.
Hi, I see two problems: 1. the WndProc method is called quite often since Windows sends messages all the time, e.g. when you move the mouse over your form; so you should make it as lightweight as possible. And you didn't: you are creating the graphics object
g
whether you will use it or not. Now creating aGraphics
is expensive, so this is slowing your form down, its responsiveness is not going to be what it could and should be. 2. The Graphics object, when you created and used it but no longer need it, should be disposed of. So you should add something likeif (g!=null) g.Dispose();
or justg.Dispose();
at some appropriate location inside WndProc (I guess just before calling releaseDC would be right, assuming you put FromHdc right in front of OnNcPaint). Failing to do so will enlarge your app's memory footprint and make the job of the garbage collector somewhat larger too. BTW: this is true for most every instance you create when the class offers a public Dispose() method; the sequence ought to be: create-use-dispose. And the bigger the object, the more reason not to forget this. Note: there is ausing
statement that often comes in handy here. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
-
Hi, I see two problems: 1. the WndProc method is called quite often since Windows sends messages all the time, e.g. when you move the mouse over your form; so you should make it as lightweight as possible. And you didn't: you are creating the graphics object
g
whether you will use it or not. Now creating aGraphics
is expensive, so this is slowing your form down, its responsiveness is not going to be what it could and should be. 2. The Graphics object, when you created and used it but no longer need it, should be disposed of. So you should add something likeif (g!=null) g.Dispose();
or justg.Dispose();
at some appropriate location inside WndProc (I guess just before calling releaseDC would be right, assuming you put FromHdc right in front of OnNcPaint). Failing to do so will enlarge your app's memory footprint and make the job of the garbage collector somewhat larger too. BTW: this is true for most every instance you create when the class offers a public Dispose() method; the sequence ought to be: create-use-dispose. And the bigger the object, the more reason not to forget this. Note: there is ausing
statement that often comes in handy here. :)Luc Pattyn [My Articles] Nil Volentibus Arduum
Ah, yes. I do tend to forget quite often to call Dispose() on objects which support it. I have been making it a habit though, I am getting better. But that did not fix the flicker. :-D
djj55: Nice but may have a permission problem Pete O'Hanlon: He has my permission to run it.
-
Ah, yes. I do tend to forget quite often to call Dispose() on objects which support it. I have been making it a habit though, I am getting better. But that did not fix the flicker. :-D
djj55: Nice but may have a permission problem Pete O'Hanlon: He has my permission to run it.
you did postpone and hence reduce the number of FromHdc calls, as I suggested? if so, is this actual code, or did you leave out some bits? you might choose to show the latest code again. and what is inside OnNcPaint itself, that could be expensive (i.e. unnecessarily slow) too. Now what exactly is flickering? the content of the form? the NC area? You may have to look into the clearing of those areas, i.e. EraseBackground and such. :) PS: I'll soon be off-line.
Luc Pattyn [My Articles] Nil Volentibus Arduum
-
you did postpone and hence reduce the number of FromHdc calls, as I suggested? if so, is this actual code, or did you leave out some bits? you might choose to show the latest code again. and what is inside OnNcPaint itself, that could be expensive (i.e. unnecessarily slow) too. Now what exactly is flickering? the content of the form? the NC area? You may have to look into the clearing of those areas, i.e. EraseBackground and such. :) PS: I'll soon be off-line.
Luc Pattyn [My Articles] Nil Volentibus Arduum
I did fix that as you suggested. And the flickering seems to be just a bit less, though nowhere near "reasonable" so to speak. I actually have a lot going on in OnNcPaint. However, even if I remove everything and add a simple FillRectangle() for the caption/title area BY ITSELF, I still get the same flicker, except ONLY in the title area which is being drawn. It only flickers wherever I tell it to draw. So it definitely has something to do with the way it is drawing, not necessarily an expensive operation. I am willing to take time on this. I have several custom controls which I am pleased with how they turned out. I figured Forms would be much more of a pain anyhow, haha. But I would sure love to figure it out. :-D
djj55: Nice but may have a permission problem Pete O'Hanlon: He has my permission to run it.
-
Hello again, CP! I am now trying to create a custom Form by painting a new title bar. I have never done this before but I did remember seeing it a lot when I was heavy into C++ and MFC. So I knew to look for WM_NCPAINT. The resources I found were telling me to override WndProc for the form and catch WM_NCPAINT, and then do my custom drawing from there. So this is what I did:
protected override void WndProc(ref Message m)
{
// Handle non-client painting
IntPtr hdc = GetWindowDC(m.HWnd);
Graphics g = null;
if ((int)hdc != 0)
g = Graphics.FromHdc(hdc);if (m.Msg == WM\_NCPAINT || m.Msg == WM\_NCACTIVATE || m.Msg == WM\_SETTEXT) { if ((int)hdc != 0) { OnNcPaint(g); ReleaseDC(m.HWnd, hdc); m.Result = (IntPtr)1; } } else base.WndProc(ref m); }
In OnNcPaint(Graphics) I make a simple call to graphics.FillRectangle, which fills the entire rectangle with a solid color (in this case, Color.Blue). However, when I resize the form I am seeing a large amount of flickering across the entire form. The background is not custom painted. I have ResizeRedraw set in the form's "SetStyle()" method. When I remove ResizeRedraw it does not paint properly at all. How can I eliminate the flicker here? This is kind of irritating. Haha. :) BTW, I have "base.WndProc(ref m);" in the "else" statement because otherwise, if I call it alongside WM_NCPAINT, it will just paint Windows' non-client area (Win7, Aero Theme). Thanks in advance.
djj55: Nice but may have a permission problem Pete O'Hanlon: He has my permission to run it.
I am not sure if this helps; I used to suppress the OnPaintBackground method in such scenarios You can try some thing like
protected override void OnPaintBackground(PaintEventArgs pevent)
{
//do not allow the background to be painted
}"Never put off until run time what you can do at compile time." - David Gries, in "Compiler Construction for Digital Computers", circa 1969.
-
I did fix that as you suggested. And the flickering seems to be just a bit less, though nowhere near "reasonable" so to speak. I actually have a lot going on in OnNcPaint. However, even if I remove everything and add a simple FillRectangle() for the caption/title area BY ITSELF, I still get the same flicker, except ONLY in the title area which is being drawn. It only flickers wherever I tell it to draw. So it definitely has something to do with the way it is drawing, not necessarily an expensive operation. I am willing to take time on this. I have several custom controls which I am pleased with how they turned out. I figured Forms would be much more of a pain anyhow, haha. But I would sure love to figure it out. :-D
djj55: Nice but may have a permission problem Pete O'Hanlon: He has my permission to run it.
Hi, without knowing much more on your exact situation, this is all I can tell you about the subject: 1. some flickering is unavoidable; the annoyance of flickering is proportional to the time it takes from the start of EraseBackGround to the end of Paint itself, and inversionally proportional to the frequency of your Paint calls. That is why shortening EraseBackGround and/or Paint is beneficial. 2. you can use double-buffering, where an unseen bitmap is operated on, and when ready the area is bitblitted to the screen (this doesn't require an EraseBackGround); either organize that yourself, or have the Control (or Form) do it for you. 3. there are a number of articles on
OnNcPaint
here at CP; I suggest you use CP search to locate and then read some of them. :)Luc Pattyn [My Articles] Nil Volentibus Arduum