I want to create an irregularily shaped/skinned window (just with rounded, alpha blended corners for now). Upon creating the top-level window i am processing the WM_CREATE message and do the following:
- Create a compatible memory DC
- Create a DIB section compatible with the window DC
- Select DIB into memory DC
- Do the drawing of my backdrop
- Apply alpha channel and premultiply RGB values
- Call UpdateLayeredWindow()
Later on I am planning on rounding of the edges by setting the alpha channel and premultiplying the bits in question to make that happen.
Now, if I create for instance a button in my window, it will not render itself. I know why, and I am trying to come up with an elegant solution to making my custom controls (child windows) here.
My initial approach was to ditch using child windows in the first place and just let the top level window do all the drawing and also input handling, hit testing, and so on. I found this to be way to tedious and instead I want to let Windows handle all this for me.
Now, I know if I create a child window, it of course behaves normally (e.g. reacting to user input), and I want to leverage this. I am planning on creating the child windows (custom controls) normally using CreateWindowEx() so they get a window handle, and recieve window messages without me having to worry about passing them manually.
Somehow I need to get these windows painted, and as I see it, the only possible way to do this is from the routine that paints the whole top level window. I need to invent some kind of logic to get the top level window's paint routine to paint my child windows whenever necessary. As far as I understand the UpdateLayeredWindow() function need to redraw the whole window.
Is it sensible to for instance have the child controls render an image of themselves that are sent to the top level window's paint routine? Like for instance the child window sending a user WM to the top level window passing pointer to its rendered bitmap as a WPARAM and pointer to a structure defining its position and size as a LPARAM.
Any better ideas? Does this make any sense at all?
Thanks, Eirik
I was trying to do a very similar thing. From reading this and other searching web. It seams the recommended mechanism for drawing a CWnd (or HWND) and it's children onto your own CDC (or HDC) is to use the printing API.
CWnd has methods Print and PrintClient and which send WM_PRINT correctly. There is also the Win32 methods: PrintWindow.
I had trouble getting this to work at first but I eventually got the right method and parameters. The code that worked for me was:
This worked for me just as is. But incase you window or children don't support WM_PRINT I looked at how it was implemented for CView class I discovered that this class provides a virtual method called OnDraw(CDC* dc) that is provided with a DC to draw with. WM_PAINT is implemented something like this:
And the WM_PAINT is implemented:
So the WM_PAINT and WM_PRINT results an OnDraw(), and the drawing code implemented once.
You can basically add this same logic your own CWnd derived class. This may not be possible using visual studio's class wizards. I had to add the following to message map block:
And my handler:
NOTE: If you add a custom WM_PRINT handler on a class that already supports this automatically then you loose the default implementation. There isn't a CWnd method for OnPrint so you have to use the Default() method to invoke the default handler.
I have't tried the following but I expect it works:
Above I defined some strange methods: ClearBackgroundAndPrepForPerPixelTransparency and CorrectPerPixelAlpha. These allow me to set the background of my dialog be semi-transparent when having the child controls be full opaque (this is my per-pixel transparency).
Here is a screen shot of my test application. When the user hovers the mouse over the "more buttons" button the dialog box is created with a semi-transparent background. The buttons "B1" to "B16" are child controls derived from CButton and are being drawn using the Print() call show above. You can see the semi-transparent background at the right hand edge of the view and between the buttons.