Download the code for this issue
Locking Window Updates
by Matthew Wilson[email protected]
A classic problem when updating windows in lengthy and granular operations is that of window flashing, where changing the contents of a control results in the repeated redrawing of part or all of its visible area. When a large number of such updates occur in a short time, this becomes problematic, and the repeated redrawing appears as a very unappealing visual disturbance.
Two common solutions to this problem are to use the API function LockWindowUpdate and the WM_SETREDRAW message.
LockWindowUpdate is implemented by swapping out the window's normal device context and replacing it with one whose visible region is empty. When LockWindowUpdate is called with NULL (which unlocks the window), the original device context is replaced, and the system invalidates an area within it equivalent (in size and location) to that of the temporary one such that the window will receive a request to redraw the modified area. Hence all drawing is effectively performed in a single operation, and the visual effect is much more seamless. The disadvantage of using LockWindowUpdate is that it can only be used for one window at a time, so it cannot be used to simultaneously lock a collection of related controls. The first window to call the function is locked, and all other calls preceding the unlock call fail. (This can be easily shown by uncommenting the artificial lock of the run button on line 93 of the test application implementation file, wndscope.cpp, which is included in this month's code archive.) Interestingly, if two versions of the application using LockWindowUpdate are run simultaneously it appears that whichever process calls the function "owns" the lock, removing the ownership of the previously successful call, which reverts to the unlocked behavior of flashing on the string inserts.
WM_SETREDRAW is a message implemented by various standard common and custom controls, including the listbox, combobox, list view, button, and tab control. It works by clearing and setting the window redraw flag. The only slight disadvantage is that the application must invalidate the window rectangle after sending the reactivating message, and therefore the whole visible window rectangle will be redrawn. Use of WM_SETREDRAW generally supersedes use of LockWindowUpdate. Of course, for windows that do not support this message, LockWindowUpdate remains the tool of choice.
Presented here are two classes from the WinSTL libraries (http://winstl.org/) window_update_scope and window_redraw_scope that provide automated scoping of these two forms of locking. (Abridged implementations of the two classes are shown in Listings 1 and 2. The full implementations are provided in the archive, and are also available online at the WinSTL site.) In the constructors of the classes the lock is set, then reset in the destructors: window_update_scope calls LockWindowUpdate in its constructor and, if successful, calls LockWindowUpdate(NULL) in its destructor; window_redraw_scope sends WM_SETREDRAW (passing False) in its constructor and sends WM_SETREDRAW (passing True). They both take the handle of the window to lock in their constructors; window_redraw_scope takes a second parameter (defaulted to True) that specifies whether the window is invalidated (via a call to InvalidateRect) when unlocked.
The uses of these two techniques are demonstrated in the accompanying application (Figure 1), which sends many items to a listbox, with or without the two locking techniques just described. To demonstrate the techniques, simply execute the program and select "Run," demonstrating the flashing. Then select, in turn, the "Use LockWindowUpdate" and "Use WM_SETREDRAW" boxes and run, demonstrating the locking.
A Better Macro Wrapper for Visual C++
By John Szakmeister[email protected]
The February 2003 "Tech Tips" column included a tip on using the do/while construct to avoid some pitfalls in defining some macros ("do/while Macros for C++" by Raja Venkataraman). This tip is useful, but I thought I'd offer my two cents to improve upon it. There is actually an easier way to get the same result. In C and C++, you can insert curly braces anywhere inside a function to section off a piece of code. I've found this ability to be extremely helpful since I do both Windows and embedded programming. Most cross-compilers do exactly what you tell them (even though some optimizations may be performed), so wrapping something with a do/while loop can actually result in code being generated for the loop. A simple way to avoid this is not to wrap it with a do-while loop, but just wrap it in braces. I also use this feature to help inline several slightly more complicated functions in C, but get to keep the speed that I desperately need to process other things.
Listing 3 shows how the original Tech Tip code would change. The wonderful (and harmful if not used correctly) feature of this technique is that you can also define local variables that will not interfere with other variables in your function. For instance, you normally can't declare a local variable after the first code statement in a .c file (Listing 4(a)). Listing 4(b), however, shows a code segment that works fine because of the new macro. Here you could even change the variable j to i and it would leave the outer variable untouched. So the code in Listing 5 would produce the following output:
i: 10 i: 100 i: 10
Displaying Full Path Names in the Recent File List Menu Item
By Pablo Presedo[email protected]
Recently, someone sent me an e-mail asking me how to get an MFC application to display the file's full path and name in the recent file list. The default behavior is to display the full path only if the current directory is different than the directory where the file is located. It will also abbreviate the path name if it is too long.
The filenames that are displayed in the recent file list are created by the CRecentFileList::GetDisplayName function. This function is called by CRecentFileList::UpdateMenu, which is called by the CWinApp::On- UpdateRecentFileMenu function. The CWinApp::OnUpdateRecentFileMenu function is called in response to the ON_UPDATE_COMMAND_UI(ID_FILE_MRU_FILE1, OnUpdateRecentFileMenu) message map entry found in appcore.cpp.
void CWinApp::OnUpdateRecentFileMenu(CCmdUI*pCmdUI) { ASSERT_VALID(this); if (m_pRecentFileList == NULL) // no MRU files pCmdUI->Enable(FALSE); else // *** This function will call GetDisplayName. *** m_pRecentFileList->UpdateMenu(pCmdUI); }
One solution to this problem is to delete the CRecentFileList* m_pRecentFileList after the LoadStdProfileSettings call made in your CWinApp derived InitInstance. We replace this with a class of our own that was derived from CRecentFileList (Listing 6). Our CRecentFileList will override the GetDisplayName function.
Make sure the nSize argument of the CRecentFileList derived class constructor is set to the value of the nMaxMRU argument of the LoadStdProfileSettings. That's all that there is to it. In this month's code archive, I have included an example that demonstrates this, as well as how to get an MFC dialog application to display a recent file list. By default, MFC dialog applications do not support a recent file list.
George Frazier is a software engineer in the System Design and Verification group at Cadence Design Systems Inc. and has been programming for Windows since 1991. He can be reached at [email protected].