views:

393

answers:

2

I have a form, which sets these styles in constructor:

this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

And I draw some rectangles in Paint event. There are no controls on the form. Hovewer, when I resize the form, there are black strips at right and bottom of the form. Is there any way to get rid of them? I've tried everything, listening for WM_ERASEBKGND in WndProc, manually drawing the form on WM_PAINT, implementing custom double buffer, etc. Is there anything else I could try?

I've found this: https://connect.microsoft.com/VisualStudio/feedback/details/522441/custom-resizing-of-system-windows-window-flickers and it looks like it is a bug in DWM, but I just hope I can do some workaround.

Please note that I must use double buffering, since I want to draw pretty intense graphic presentation in the Paint event. I develop in C# .NET 2.0, Win7.

Status Update 1

I've managed to get rid of most of the black stripes by implementing the resize functionality by myself. Hovewer there are still some minor glitches. Is there any way to do resize and paint operation at once? Here is a pseudo-code of what I need to do:

IntPtr hDC;
var size = new Size(250, 200);
IntPtr handle = API.PaintAndResizeBegin(this.Handle /* Form.Handle */,
                                        size.Width, size.Height, out hDC);
using (var g = Graphics.FromHdc(hDC)) {
    this.backBuffer.Render(g, size);
}
API.PaintAndResizeCommit(handle);

Is there any way to implement the above code?

The second solution could be to back-buffer whole form, including non-client area. But how to do that? I don't want to paint the non-client area by myself, as I want to keep the nice aero effect on Vista/7. Any help will be deeply appreciated.

Status Update 2

It looks like this problem is unsolvable, since it is omnipresent on Windows, in every application. We can just hope that MS will take some inspiration in Mac OS X and will provide appropriate APIs in new Windows.

A: 

I found that it is best not to do any custom rendering directly on the Form surface. Instead, put a docked PictureBox on the form, create Bitmap object that will be displayed in the PictureBox, draw everything onto that using the System.Drawing.Graphics.FromImage(Image) method.

I used that method with a game loop to make a simple shooter game (Crimsonland-style) and got pretty good performance (with anti-aliased lines), above 100 FPS.

Allon Guralnek
And the black stripes disappeared when you were resizing window? If I create just a blank Form, the resizing is OK, no black stripes. But if I add a few controls or do some not-so-fast painting in OnPaint, the black stripes appear if you drag the form's corner and resize it as fast as you can. And it looks like those stripes are drawn by system, not my app, since almost every app have this "feature". And if there are no black stripes, then the controls are for just a fraction of second beyond the left/bottom corner of form during resizing.
Paja
When using the `PictureBox` and `Bitmap` method, you don't need to set any `ControlStyles`. When creating the bitmap on which you draw, you must specify a fixed size. If the user resized the window, you have to recreate the bitmap with the window's new size. There will be a gray fill to the expanding form (or whatever color you set the `BackColor` of the `Form`), until you release the mouse, and then a ResizeEnd event will fire, where you have to recreate the bitmap. In any case, there are no black bars (unless you set the form's `BackColor` to black).
Allon Guralnek
Well the bars have the color of the `BackColor`, but it is not important. There are the bars! If they are gray or black doesn't matter. I just want fluent resizing without any graphical glitches, as on Mac OS X. Just try to resize some window very fast on Mac OS X and on Windows 7, and you will see what I mean.
Paja
Well then you can perform the recreation on `Resize` instead of `ResizeEnd`, but that'll be somewhat slow and not as smooth as you'd like. For that kind of performance, you can't use GDI for the heavy lifting (i.e. using the Graphics or Bitmap classes). Instead, use hardware accelerated APIs (which is what MacOS and Vista/7 use for the smooth everything). For .NET, have a look at WPF (or even XNA). You can get butter-smooth rendering with those, and you'll get your resizing with 0 lines of code.
Allon Guralnek
Well, VS2010 is written in WPF, and it has EXACTLY the same black-strip-on-resize problem. Just open VS2010, do not open any project, leave property-window docked at left side and try dramatically resizing the window. But you are probably right, GDI is just not fast enough. So using WPF and my own resize implementation could fix the problem.
Paja
AFAIK, in VS2010 only the code editor and a few other miscellenious things are rendered in WPF. The rest is GDI.
Allon Guralnek
Well I quote Wiki (http://en.wikipedia.org/wiki/Microsoft_Visual_Studio): `The IDE shell has been rewritten using the Windows Presentation Foundation (WPF)...`
Paja
In any case, I'm resizing VS2008 on Vista and see no black bars (or bars of any color). The resize is just lagging - I move the mouse to new coordinates and it takes a little while before the resize actually occurs - since it has to reflow and re-render all the IDE elements. Bottom line is, when the user resizes a window there are many intermediary sizes, all of which have to be rendered. The rendering can't keep up so there are two options: fill the expanded area with something (such as a black background) but maintain a smooth resize for user, or delay the resize until drawing is complete.
Allon Guralnek
Well, Mac OS X just doesn't have this problem, so I'm curious why Win does. I think that on Windows resizing the window frame is done by system, but repainting the content by the application. So if you resize the window, then frame gets sized as fast as possible, but there is a delay when the app paints the window content. So I've written my own resize and disabled the system resize, which reduced majority of glitches, but there are still some minor problems.
Paja
BTW, there are always bars. Is you quickly resize the VS2008 vertically down, you will see that for a fraction of second, the status bar is just a few pixels ABOVE the bottom border, thus there is a color bar between the border and status bar. As I said, on Mac OS X, there is NEVER THAT SPACE! Why does Win have these problems, but Mac OS X doesn't? I've read somewhere that in OS X, the windows are composited, but in Vista or 7 they are too.
Paja
Because with Microsoft, backwards compatibility is paramount. They go to incredible lengths to make sure that new versions don't break anything. The most extreme example is Windows. APIs are deprecated, but never removed - including GDI in Windows 7. There are many advantages to that, and in my opinion, one of the main reasons Windows has become such a ubiquitous platform (e.g. Win7 x86 can STILL run 20-year old 16-bit Win 3.x apps). The disadvantage are also quite obvious - baggage, and lots of it. That baggage - the use and reliance of old drawing APIs - is the culprit of what you describe.
Allon Guralnek
Well but no-one is stopping MS from adding new API, which won't have such problems. So new applications could use that API and behave without any glitches, and the old applications will behave as before. BTW API is not deprecated. :-) For example DwmExtendFrameIntoClientArea, which extends glass into client area, is available only as API.
Paja
True, which is exactly what they're doing - adding newer, better APIs. But it doesn't prevent people from continuing to use older APIs, which is why you still see glitches and poor performance. Of course API as a concept is not deprecated (it can't, that's the only way applications can communicate), I was talking about certain APIs being deprecated, and being replaced by newer APIs. When a new version of MacOS comes along with all the old stuff removed, apps break, but Apples users and developers are used to that - for better (no baggage) and for worse (breakage), and vice versa for Windows.
Allon Guralnek
Yeah, but this resizing bug is present since first version of Win, and the API to prevent this still doesn't exist in Win 7. Or does it?
Paja
EDIT: Not since first version of Win, but since first version of Win with live update while resizing.
Paja
Like I already said, I believe a WPF app won't have those issues, but I could be wrong.
Allon Guralnek
They have. :-) http://www.vertigo.com/FamilyShow.aspx This app is written in WPF, and has exactly the same resizing problems as any other app on Windows.
Paja
A: 

I've found the function which can paint and resize window at the same time - UpdateLayeredWindow.

So now it should be possible to create resizable windows, which do not have any strips while being resized. However, you need to paint the window content yourself, so it is a little inconvenient. But I think that using WPF and UpdateLayeredWindow, there shouldn't be any problem.


Update

Found problems. :-) When using UpdateLayeredWindow, you must paint the window's border yourself. So, if you want standard window painted using UpdateLayeredWindow with nice glass effect in win7, you are screwed.

On Microsft Connect is even a thread about this problem, where Microsoft says it is a bug by design, and if it ever gets fixed, then probably in Win8 or some newer system. So there isn't much we could do about this.

Paja