As I’ve mentioned before, I’ve been fuddling around doing UI programming recently – it’s a bit different from my usual work deep in the bowels of the <insert whatever subsystem Larry happens to be working on (redirector, audio stack, POP3 server, whatever)>.
I’ve been having a great deal of fun doing the whole “UI programmer” thingy (although my grandboss is right – once you start doing UI, everyone thinks that you’ve done it wrong) recently, and I’ve learned a ton of stuff.
Most of the UI I work with is written as dialog box applications – for various reasons, many of the audio UI elements are actually dialog boxes. As such, the system controls the actual drawing of the UI elements, especially if you specify the WS_CLIPCHILDREN window style.
I recently had a bug where one of the UI elements wasn’t being painted correctly. For a number of reasons, the element was being overwritten by another element.
It took a lot of work to get to the scenario where the UI element got messed up, so I was going to ignore the problem (it was ugly but transient). But then I got to browsing the web and I ran into this article. The article had nothing to do with the order of painting in a dialog, but it DID mention a windows message that I hadn’t heard of before, WM_PRINTCLIENT.
Hmm, that’s interesting. WM_PRINTCLIENT says “The WM_PRINTCLIENT message is sent to a window to request that it draw its client area in the specified device context, most commonly in a printer device context.”. Ooh, that’s interesting. If I can just send a WM_PRINTCLIENT to each of the controls in the window, maybe I can control the order in which the controls are rendered, which would allow me to fix the problem (and remove a long-standing issue with the program in question).
On Monday of last week, I came in and decided that I’d dedicate a couple of days to see if my idea might work – worst case I’d lose a couple of days of work, but if it did work it would be really cool.
By mid-afternoon on Monday, I had come to the point where I realized that my crazy idea would actually work.
Skip forward one very long week of debugging and tweaking and I had fixed the problem – I had rebuilt the painting algorithm for this application and it worked!
Right now I’m highly enamored of the WM_PRINTCLIENT message simply because it’s so darned useful in scenarios like mine.
PS: It’s possible that I might be able to achieve similar results with the WS_EX_COMPOSITED window style but it’s not clear given some of the UI requirements for the application – I may play around with that idea over the next couple of weeks.
 One of the reasons for the apps being dialog box based is that it makes it easier for localizers if the application is built on dialog boxes because it gives the localizers greater flexibility when translating the OS – if the preferred text in the destination language doesn’t fit, they can adjust the layout of the dialog to make the text fit.
WM_PRINTCLIENT is a mixed blessing:
* If you mix WM_PRINTCLIENT and standard WM_PAINT rendering (e.g. only use WM_PRINTCLIENT during resize and fall back on normal WM_PAINT outside of the resize loop) then you'll see the borders of some controls flicker between two styles. (The listview controls on Vista are an example.)
* Some controls don't handle WM_PRINTCLIENT at all. Some handle it but look completely different to their normal WM_PAINT mode (more so than just the border styles).
* If your painting is completely based on WM_PRINTCLIENT then you'll lose all of the "hot" control animation stuff on Vista. (e.g. The default pushbutton won't glow/pulse.)
Leo: Actually you can get much of the themed animation stuff to work without that much work. For the controls where I wanted the themed animation to continue to work, I simply didn't disable the WM_PRINT handler. It worked well enough.
If you look around, you'll find documentation that says that the controls that don't handle the WM_PRINTCLIENT message DO handle the WM_PRINT message with WPARAM set to a HDC.
One other crazy tricky piece is the WM_HSCROLL and WM_VSCROLL styles - you simply can't get them to work with WM_PRINTCLIENT because they're in the non client region.
To get the scrollbar control to work, I had to create a scrollbar control manually.
Doesn't that mean that you're drawing the controls more than once? Why can't you just set the controls to the correct z-order, or (better) ensure they don't overlap?
Miral: It's complicated :). Fixing the Z order isn't sufficient for this application. There is stuff that has to be painted across multiple controls, with different clipping regions.
Is there a good way to provoke flickering bugs, as in making them easier to see? On today's machines flickering is often times hard to notice.
Make sure you test with and without desktop compositing enabled. I'm finding a lot of apps targeted for Vista behave horribly when compositing is disabled.
WM_PRINTCLIENT rocks! I've been using it to make double-buffered control drawing happen for years... it's shocking how much nicer most common controls look if you don't have to watch them draw themselves periodically.
I'm a contrarian here. I like what you added to our UI. Do I need to go talk to your grandboss again? ;)
Adam, actually his comment was really about "everyone's a critic".
I'm not sure he's seen the two most recent additions to the UI, but honestly I think he'd be pretty pleased with the changes. I know that my boss is extremely happy with them.
Most of the big changes I made will be in the next public release of Win7 (whenever that is), and yes, I'll point the changes out when it's public.
Yesterday I mentioned WM_PRINTCLIENT and how awesome it is when trying to strictly control the drawing
Might be overkill, but if you're doing custom overlays and the like, another way to do it would be to go control-less (draw everything manually rather than actually creating subcontrols), or use WPF (which basically does the same thing in an easier-to-use manner).
Miral, I mentioned above why we use a dialog box - it makes certain operations like localization possible - if we did our own layout it wouldn't be localizable.
There are other benefits as well, but localization is the big one.
Strange, we do very complex control skinning and we've used PrintClient only in a few case to replace the background of an edit control, but never to fix to a flicker or bad redraw. One thing that's to know is that some window control have the CS_PARENTDC style (at least on XP), an optimization that causes problems if you ignore it and combine with WM_CLIPSIBBLING, if I recall correctly. Basically, they really want to be painting directly on the dialog box's background, in z order. When you try to get creative with the dialogbox background and attempt to draw the control transparently, you need to wrap your head around this. I can't recall the gotchas. Indeed you need to check with and without Vista's Aero.
Dialog box or not, there can be many reasons for the size of the text to vary so you'd better be prepared to compute the size of each control at the time of drawing.
If you find those computations too expensive then you might make a conscious decision to let text get clipped instead. Let it be a conscious decision so that you'll be properly prepared to ignore subsequent complaints.
Sometimes even runtime computations aren't enough. I had some text clipped on Vista that wasn't clipped on XP, for no reason that I could ever discover. I had to just add a few pixels to the width.
Larry - just wondering... can you give a more detailed scenario? This solution seems.. well... wrong to me. WM_PRINTCLIENT is usually a lot more hassle than it's worth, and from the description, it sounds like what you need is the WS_CLIPSIBLINGS style on the controls on the dialog.