I have a fairly standard MFC application that consists of a main window, and occasionally brings up modal dialogs. As we all know nothing can be done outside a modal dialog until it is closed.
Therefore, a nice UI feature is to "dim" the rest of the main window behind the dialog, to visually indicate you can't use it until you're done with the modal dialog. Some web apps and java/mac apps do this, but I've never seen it done in a traditional C++/MFC application. I'd like to give it a try, even if it's unusual for the platform.
How can this be done? I have several modal dialogs in the application, used in this pattern:
// pMainFrame is available as a pointer to the CWnd of the main window
CMyDialog dialog;
dialog.DoModal(); // invoke modal dialog; returns after dialog closed
Is there an easy way to have the window dimmed before any DoModal() and restored afterwards? I'm using Visual Studio 2010 in case the updated MFC has any features that might help.
Edit: I've posted a solution based on oystein's answer, but I'm starting a bounty in case anyone can improve on it - especially with a smooth fade in/fade out.
I've accepted oystein's answer, since it led me to the solution, but I thought I'd post my modifications. I had to modify it a bit to make it work for me, so it might come in useful to someone else.
For the record, the dimming works well, but it doesn't look as natural as I hoped. In an application which frequently brings up dialogs, the dimming becomes distracting in its regularity of seemingly switching the main window on and off. To compromise, I've made the dimming fairly subtle (about 25% opacity) which gently highlights the active dialog; the instant dimming is still a little distracting, but I'm not sure how to have it fade in or fade out smoothly, especially when scoped.
Also, I'm not a UI expert, but the dimming gave me a sort of impression that the dialog was less related to the window content behind it. This made it feel a bit detached from what I was working on in the application, even though the dialogs are directly manipulating that content. This might be another distraction.
Here it is anyway:
CDimWnd.h
CDimWnd.cpp
Usage is dead simple: because CDimWnd creates itself in its constructor, all you need to do is add
CDimWnd dimmer
as a member of the dialog class, and it automatically dims the main window, no matter where you call the dialog from.You can also use it within a scope to dim system modal dialogs:
I couldn't resist doing it.
It's your code with added timers and implemented a fade in / fade out. Also I changed to use mid grey rather than black for the obscuring block.
You can tweak the constants that control the fade to make it smoother by increasing the duration or the increasing the rate. Experiment shows me that a rate of 10hz is smooth for me, but YMMV
You can create another window, completely black, on top of the window you want to dim, and set the black window's opacity with SetLayeredWindowAttributes. It doesn't have to be black, of course, but I guess that's the best dimming color.
EDIT: I hacked together an example - but note that I am not an MFC developer, I usually use the Windows API directly. It seems to work okay, though. Here is a pastebin. Feel free to add fade-ins etc. yourself. Also note that this dims the entire screen, you'll have to resize my dimming-window if you don't want this behaviour. See code comments.
UPDATE:
I hacked together some more code for you, to handle the fading. I'm still no MFC dev, and I left the code in a "rough" state (little error handling, not very robust) to give you something to do too. :) Anyway, here's one way to do it, that I think is fairly clean:
To use it, make your main window contain a dimmer window
It can then be used e.g. like this:
Alternatively I guess you could put this code (
Show()
/Hide()
calls) in the constructor and destructor of the modal dialog, if you want to keep the code there. If you want a "scope"-dim, like in the example you posted, this code would have to go in the constructor & destructor of the CDimWnd class, and you would need something like a static member variable to ensure that only one dimmer is running at a time (unless you want to use a global variable).For the dimmer window - I did this:
CDimWnd.h
CDimWnd.cpp
Okay. As I said, this was thrown together fairly quickly and is in a rough state, but it should give you some code to work from, and a general idea of how (I think) timers are used in MFC. I am definitely not the right person to think anything about that, though :)