Location of a maximized window with custom chrome - windows

I've created a window with a custom chrome by:
Handling WM_NCHITTEST and returning the appropriate values for the window caption and borders,
Swallowing WM_NCPAINT (and WM_NCUAHDRAWCAPTION, WM_NCUAHDRAWFRAME),
Handling WM_SETTEXT, WM_SETICON and WM_NCACTIVATE with DefWindowProc by temporarily removing WM_VISIBLE,
Handling WM_WINDOWPOSCHANGED to apply a window region for rounded corners.
Moving/Resizing is handled fine by Windows. I added WS_THICKFRAME to the window styles to enable Aero Snap functionality. But now when the window is maximized it is positioned at (-5,-5) and my custom titlebar is cut off (this doesn't happen without WS_THICKFRAME).
What is the proper way to fix that? What influences that position? It seems to be unrelated to values returned for WM_NCHITTEST.

Late answer, but it seems like noone else has answered.
You can check when the window is maximized and account for the margin when the window is maximized. You may want to handle WM_NCCALCSIZE rather than WM_NCPAINT to make the entire window appear as client area, rather than painting the non-client area.
This describes one approach to implementing custom chrome (I was the author):
http://blogs.msdn.com/b/wpfsdk/archive/2008/09/08/custom-window-chrome-in-wpf.aspx
Hope that helps,

Related

The correct method for drawing a bitmap image into a window

I have a function which takes a rectangular region of a bitmap image, rescales it to different dimensions, and draws it at some offset inside of a window within my dialog-box application:
void DrawImage(HANDLE hImageBitmap,
CDC* pDstDC,const CRect& dstRect,
CDC* pSrcDC,const CRect& srcRect)
{
pSrcDC->SelectObject(hImageBitmap);
pDstDC->SetStretchBltMode(HALFTONE);
pDstDC->StretchBlt
(
dstRect.left,dstRect.top,dstRect.Width(),dstRect.Height(),pSrcDC,
srcRect.left,srcRect.top,srcRect.Width(),srcRect.Height(),SRCCOPY
);
}
I create and maintain the window using a CWnd m_cImageWindow member variable.
I perform the drawing from the dialog-box's OnPaint handler as follows:
CDC* pDC = m_cImageWindow.GetDC();
CDC cDC;
cDC.CreateCompatibleDC(pDC);
CRect srcRect = ...;
CRect dstRect = ...;
DrawImage(m_hImageBitmap,pDC,dstRect,&cDC,srcRect);
cDC.DeleteDC();
m_cImageWindow.ReleaseDC(pDC);
I have two problems:
I see flickering whenever I change the drawing parameters. The standard way to solve this, from what I have read here and there, is by using a temporary DC for double-buffering. But as far as I understand, this is exactly what I am already doing.
If some of the destination region falls outside the window, it is painted over other controls within the dialog box. I am able to partially solve this by calling MoveWindow or SetWindowPos for each one of these controls. But I can still see the image flickering behind them. I have tried calling SetWindowPos in various different ways, hoping in vain that it would dictate a strict Z-order of the controls.
Thank you.
The painting of the image into the child window should be done in the WM_PAINT handler for that child window, not for the dialog. Your child window may need remember information provided by the parent dialog so that it can paint independently. By painting the window from the dialog's WM_PAINT handler, you're possibly painting more often than necessary (and possibly aren't causing a validation to occur in the image window).
The dialog should probably have the WS_CLIPCHILDREN window style and your image window should probably have WS_CLIPSIBLINGS. This will prevent the dialog controls from drawing over each other, and it can reduce flicker by allowing for more minimal updates.
If the image will always completely cover the entire image window, then you want to make sure there's no background erasing happening for the image window, as that can cause a flash of the background color which looks like painting. There are several ways to do this, but the easiest is probably to provide a WM_ERASEBKGND handler that just returns TRUE.
I found OnEraseBkgnd to be the right place to minimize flickering of drawn bitmaps.

When (and how) to lay out the children of a Win32 window in response to a resize?

Windows sends several messages when a window is resized:
WM_GETMINMAXINFO
WM_ENTERSIZEMOVE
WM_EXITSIZEMOVE
WM_NCCALCSIZE
WM_SIZING
WM_SIZE
WM_WINDOWPOSCHANGING
WM_WINDOWPOSCHANGED
and possibly more.
If I would like to re-position the children when my window is resized, where and how should I do so?
I'm looking for the "best" method -- i.e. the method with the fewest gotcha's and the least flicker.
My current method is to perform all the repositioning inside WM_NCCALCSIZE, using DeferWindowPos.
However, I've also tried handling it inside WM_WINDOWPOSCHANGED... but no matter where I handle it, it seems like there is always at least one "moment" when the window is painted in an in-between state, where the window's size has changed, but its contents have yet to be resized.
Another effect I would also like to avoid: moving a child after it has already moved. I don't want the user to see a control sliding down and then back up because of my change -- it should have as few transient effects as possible.
Am I doing this correctly? Is there a better place I can lay out the window's children?
You should re-position the window in the WM_SIZE message, because that is the last one that the window recieves before complete it's task...To re-position the window you can use MoveWindow

How to draw OpenGL content while resizing win32 window

While resizing win32 window, with OpenGL context, it just shows black on the newly exposed area.
I do get WM_PAINT message while resizing, and I do try to render new content, but it seems as if SwapBuffers does nothing, while resizing.
How should window resizes be handled correctly, so that there is no "broken" content while resizing?
This usually happens if you have a background brush configured for your window's class (see the WNDCLASS or WNDCLASSEX structure). If there's a brush, the system will clear the window right after each redraw step, then send the WM_PAINT. In case of V-Synced SwapBuffers your picture may have been overdrawn by the next resizing step before the buffer swap happened, or just right after it, but before that part of the screen was sent to the display device.
Either way, the solution is to set the background brush of the window to NULL. Also tinkering with the WM_ERASEBKGND message handling may give results.
EDIT due to comment
If the content of the last frame stays visible, you probably just don't react to resizing with a redraw. The easiest solution to this is calling the drawing function from the WM_SIZING (or the WM_SIZE, just try both) message handler.

Correct way to do transparent buttons in WINAPI

How do you make the button with overrided WM_PAINT transparent. So that you could see the form through it in all places except where something is drawn. At the same time avoiding the flicker.
I saw examples with SetBkMode(HDC, TRANSPARENT), using NULL_BRUSH, examples with CompatibleDC and BitBlts, but I'm not quite sure which is the correct way to go, and also, how it behaves when WM_CLIPCHILDREN is set on the parent window. Most of the experiments I did had some weird behavior as well. Can't find a good documentation on WM_ERASEBKGND/WM_CTLCOLOR/WM_PAINT/WS_EX_COMPOSITED/WS_CLIPCHILDREN/etc internal relations on MSDN at all.
Does anyone know where I could read about this topic with all the gotchas associated?
mmm, I've never found anything close to a authoritative document on this topic.
This just serves as my rather random memory dump trying to get controls to "play nice", when animated, on a window that was either skinned (normal non client area with a bitmap background), layered (to get a window with custom non client edges with drop shadow effects) or with extended Aero Glass (via the DwmExtendFrameIntoClient) effects.
SetBKMode(... TRANSPARENT) just ensures that text rendering does not fill in the background of the text with the current bk color.
WS_EX_COMPOSITED causes windows to paint the parent and all child windows to a back buffer when the parent is invalidated, and then paint the back buffer. This is flicker free, but NT 6.0 introduced the desktop window manager which does not honor WS_EX_COMPOSITED.
WS_CLIPCHILDREN prevents the child windows and the parent window painting the same area twice. But is contra indicated if you need to use group boxes, or tab controls.
WS_CLIPSIBLINGs could be useful if child windows overlap and cause flicker. again this style is useless if you need to use group boxes or tab controls.
The other problem with WS_CLIPCHILDREN is you can't paint a background in the parent widnows window proc and rely on a NULL brush to reveal the skin. You can return a brush from WM_CTLCOLORxxx messages to coerce some of the standard controls to paint their background with your skin bitmap.
WS_EX_LAYERED is another style that makes windows buffer the painting of your parent window. But the layered windows painter does not paint child windows at all, so you need to manually paint the child windows by sending WM_PRINTCLIENT messages. Not all controls support this message.
WPF gets around the lack of back buffered painting and alpha support by not creating actual child windows at all for its buttons.
Final take on the situation:
With a little work you can get a skin behind most standard controls easily. WS_CLIPCHILDREN and no background painting on the parent will mimimize flicker. Handle WM_CTLCOLORxxx to fill the background on the controls.
If you are using Group Boxes or TabControls to frame other controls you absolutely must get the Z-order correct if using WS_CLIPSIBLINGS.
By sending controls WM_PRINTCLIENT messages, and some subclassing, you can get standard controls to paint onto a DIBSection, which you can then manually (or use DWM worker functions) repair the alpha channel of, and then paint onto a layered window, or a window with extended aero glass. This is even flicker free, but controls that don't support WM_PRINTCLIENT, or frequently update themselves outside of WM_PAINT, will not display correctly.

How to draw outside a window?

Looking at a Windows tooltips class hint window, i see that it draws its drop-shadow outside the hint window's actual rectangle.
Using SpyXX - i can get the tooltip's window rect, and class styles:
Rectangle: (440, 229)-(544, 249), 104x20
Restored Rect: (440, 229)-(544, 249), 104x20
Client Rect: (0, 0)-(104, 20), 104x20
You'll notice that the drop shadow you see is physically outside the window that's being drawn. How can i draw a shadow outside around my window, while being outside my window?
Note: The shadow is not drawn using the standard CS_DROPSHADOW class style. i've confirmed this experimentally, and can also see the class style's for the window in SpyXX; it does not use CS_DROPSHADOW:
Windows Styles: 94000001
WS_POPUP 80000000
WS_VISIBLE 10000000
WS_CLIPSIBLINGS 4000000
TTS_ALWAYSTIP 1
Extended Styles: 00080088
WS_EX_LAYERED 80000
WS_EX_TOOLWIN 80
WS_EX_TOPMOST 8
So how can i draw outside my window?
Note: Trying to draw on the desktop DC is out. From Greg Schechter's Redirecting GDI, DirectX, and WPF applications:
Drawing To and Reading From the Screen
-- Baaaad!
Lastly, since we're on the redirection
topic, one particularly dangerous
practice is writing to the screen,
either through the use of GetDC(NULL)
and writing to that, or attempting to
do XOR rubber-band lines, etc. There
are two big reasons that writing to
the screen is bad:
It's expensive... writing to the
screen itself isn't expensive, but it
is almost always accompanied by
reading from the screen because one
typically does read-modify-write
operations like XOR when writing to
the screen. Reading from the video
memory surface is very expensive,
requires synchronization with the DWM,
and stalls the entire GPU pipe, as
well as the DWM application pipe.
It's unpredictable... if you somehow
manage to get to the actual primary
and write to it, there can be no
predictability as to how long what you
wrote to the primary will remain on
screen. Since the UCE doesn't know
about it, it may get cleared in the
next frame refresh, or it may persist
for a very long time, depending on
what else needs to be updated on the
screen. (We really don't allow direct
writing to the primary anyhow, for
that very reason... if you try to
access the DirectDraw primary, for
instance, the DWM will turn off until
the accessing application exits)
You can't draw outside your window in the manner you describe.
If you right click your desktop then go to properties/appearance/effects and uncheck 'Show shadows under menus' ... you will no longer have the shadow.
Bottom line is that this is a product of the window manager not your program.
Q: How do you draw outside of one window? A: Draw inside another window!
First thing to note is that the tooltip class actually does use the CS_DROPSHADOW style - but note that this is a class style, not a window style, so you have to look at the Class tab in the Spy++ properties dialog to find it. You'll see that the tooltips_class32 windows does indeed have this - and a few others.
But that just leads to the next question - how does that work? Well, it seems that Windows implements this by creating a helper HWND to draw the shadow - presumably it's creating another popup window the same size and shape as the one it's shadowing, filling it with gray, placing it directly underneath the main window, and setting it as a WS_EX_LAYERED window so that the shadow can be transparent and fade out around the edges using alpha-blending. And there's nothing to stop you from using the same or similar techniques yourself if you want to add a different type of shadow effect to one of your own windows.
So, long story short: if you want to draw outside of your own window, create a helper transparent window in the general area that you want to draw on, and draw on that helper window instead.
--
Now, if you try to find one of these helper shadow windows in Spy++, you won't find much. Unlike the tooltip_class32 windows, which are long-lived and just hide/show themselves as needed, these shadow windows are a more elusive creature: they are only created for as long as needed, so you'd have to refresh Spy++ while there's a tooltip or popup menu or other window using the shadow present - and that's tricky, since most tooltips and menus will disappear as soon as you move the mouse to switch to Spy++. But it turns out that the tooltips on Spy++'s own toolbar will stick around: so start Spy++, hover over an item in the toolbar, and hit F5 to refresh the HWND tree while the tooltip and shadow are present. Now scroll down, and you should see the third and fourth visible HWNDs in the tree are the tooltip itself, and right after that, a SysShadow window. Unfortunately, since the tooltip and shadow have by now disappeared, if you attempt to get the properties dialog for that HWND, you'll get a get a blank property dialog with an 'Invalid Window' message. If you really want to poke around and see how that SysShadow works, what styles it itself uses and so on, you could create a target app with a long-lived popup that uses CS_DROPSHADOW that you can then explore in Spy++ at leisure.
(Finally, note that these shadows are a completely different thing than the shadows that you see when one app window is on top of another above another since Vista: this type of shadow is part of Aero Glass mode, and handled by the same Desktop Composition Manager that adds the glass titlebar effect, and it doesn't use or need helper windows to implement the shadows.)
I wouldn't be surprised if that shadow is intimately tied to the window manager itself; it is after all the window manager who decides what window gets to paint which parts of itself and when it can do it. I don't see it as rocket science to paint that shadow if control over all that is gained, which the window manager has.

Resources