From time to time even the best applications need a design update. This helps both the design company and the end user. The end user benefits from using a refreshed, updated tool that reflects current technology and appears newer aesthetically. The development team, on the other hand, get some extra cash for the modern design. If you have an older application developed under MFC, this article series will be perfect for you.
Contributed by Gabor Bernat Rating: / 5 October 13, 2009
You may recall that some time back I wrote an article about how to upgrade your MFC program with just a recompile under Vista. You can find this article here. In that article I tried to present what kind of a "new look" update you can get with no or minimal coding effort after the release of the MFC Feature Pack with the Visual Studio 2008 Service Pack 1. This time I will delve more into the technical parts and focus on the new controls, additions and improvements that you can find in this pack.
Read on only if you have good knowledge of MFC. You should understand how it works, with its old classes and message maps. When I write "old," I refer to the ones present prior to the MFC Feature Pack. You can find tons of information on this topic in most of the books released about MFC.
You'll find relatively little information, however, about the new items in the feature pack. MSDN gives you a good idea of how to upgrade your application through three articles. Nevertheless, at least in my opinion, it fails to present the new additions in depth. The series I start today will try to put an end to this.
For starters, I will go into some basic information you need on how to upgrade an old application. What better method is there than using an old application's source? To point out that any application that uses MFC will do it, I have chosen a project build with Visual Studio 6.
It is an open source project, so anyone can have access to it and play around with it. I used XML Explorer. This is "A utility to query xml files using XPath and also extend XPath to more documents than one." Ignoring the XPath and XML part, we are going to work with how it looks. As part of maintaining the open-source license, I am going to post the source code for what I change. If I managed to raise your interest, then click the link to the next page button and keep reading.
For comparison, first let us see what we have. Build and run the source code you've downloaded from the site. It will pop up a couple of minor errors like the call for the 'MSXML.DLL' library. Download it from here and copy it into the project directory. Compile again to find another two errors and three warnings.
In the case of the iomanip.h, remove the .h extension, as the header that has been included in the standards the extension is no longer required. Moreover, it had been deprecated. The unreferenced local variables are not required. Comment them out, or delete them.
Do the same thing with the ambiguous CString initialization (call). The sprint function is also deprecated. Use the scanf_s function instead (you should always use the safe functions). From xmltree.cpp you can remove the Enable3dControlsStatic() function call, as it is no longer required.
There also exists a functionality glitch. Try to load something and you will observe that, in fact, nothing happens. The issue is within the LoadXML function. Search for, inside the xmltrrDoc.cpp, the following code snippet:
hr = CoInitialize(NULL);
if(!SUCCEEDED(hr))
return 0;
The problem is that apparently, the initialization has already been made; because of this, an error message is returned. However, this is not a fatal error. It can be ignored. Replace the test lines (last two) with the following snippet:
if (FAILED(hr))
{
if (hr != RPC_E_CHANGED_MODE)
{
return 0;
}
}
With these modifications the program should be up and running, like this:
I will use Visual Studio 2008 with Service Pack 1 installed. Open the XML Tree project and agree to the conversion. Through the update process, there have been introduced approximately 120 new classes in the feature pack. Therefore, we need to make sure that these have been included in the precompiled header: stdafx.h. Enter the following line into this:
#include <afxcontrolbars.h>
Some of the basic items of the MFC library have been extended. To use them we need to derive from this. In the case of these important headers, BCGSoft (the builder of the expansion pack) just added an Ex ending. Replace the CWinApp with CWinAppEx, CMDIFrameWnd with CMDIFrameWndEx and CMDIChildWnd with CMDIChildWndEx. If you have the CSplitterWnd class, replace it with CSplitterWndEx. For fast replacement, use the Quick Replace tool for the Entire Solution with the "Match Whole Word" option.
Once we use these new classes, we need to extend our resource. We need to do this because these classes use internally a couple of string definitions from a string table (such as the close tooltip text, and so on). Go to the Resource View and right click and open the resource includes dialog. Append the "afxribbon.rc" as you see below:
New base classes also require some new classes. Go to the Mainframe.cpp and modify the control-embedded members from CStatusBar to CMFCStatusBar and the CToolbar to CMFCToolbar. You no longer need to call the GetToolBarCtrl () function to control the newly-extended toolbar class. You can call the functions directly. Make the appropriate modifications.
Before we try to compile the program, modify the DockControlBar call from the OnCreate in the mainframe class to the DockPane. Practically every ControlBar (or Bar) name in functions has been renamed to Pane. This is the case for the GetBarStyle to GetPanestyle and for multiple other functions. The program should now look like this:
Now we will extend the applications with a little functionality. For this, we use the application's main class: Xmltree. In this, at the end of the InitInstace function (before the return, of course), you should append the following code snippet:
InitContextMenuManager();
InitShellManager();
InitKeyboardManager();
InitTooltipManager();
CMFCToolTipInfo ttParams;
ttParams.m_bVislManagerTheme = TRUE;
theApp.GetTooltipManager()->
SetTooltipParams(AFX_TOOLTIP_TYPE_ALL,
RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams);
The first call initializes a context menu manager for your application. With this, you can easily create and manage context menus for your application. The second one creates a CShellManage object with which you can perform various shell functions, such as copy tasks and viewing the file system.
The third call creates a CKeyBoardManager object with which you can load and save your accelerator keys directly to the register. The fourth call creates a CTooltipManager object, with which you can manage the appearance of the tooltips, naturally. These four calls all send back the pointer for the manager. If you want to use them, you may want to save them. However, you can ask the application for them whenever you require them.
With the last four lines, we specify that the application will take care of the tooltips' appearances. First, we set the variable, and after that, just ask the pointer and call its function to set it. Now every tooltip request will need a CMFCToolTipInfo Class from the visual manager to appear on the screen.
Most of the settings for the application have been moved from the "ini" files to the registry. To make this change, we need to add the following two commands after the LoadProfileString of the application class:
SetRegistryKey(_T("MFCUpdateXMLTree"));
SetRegistryBase(_T("Settings"));
In our case, the first is already present. Replace it with our custom version. For the status bar, there are a couple of new additions, like the appearance of images, progress bar, double click response and so on. Check profile to read and find out more about them. As a small teaser to show what you can do with them, look at the screen shot below (done for another application):
Finally, today we will apply a generic style to our application. We will apply to our program the Office 2007 look. I will code it for the Black look; however, it will be general enough for you to change it to any of the options: blue, silver or aqua.
Declare the variable of the application as extern in order to access it from other classes within the project. After the class declaration, add the following lines:
extern CXmltreeApp theApp;
Add a UINT variable to the App class with the name of m_nAppLook. Make it public. In the Mainframes constructor, we initialize this with the default value of black style. First, we need to define the styles. Open Resources.h and add a number definition for every style:
#define ID_VIEW_APPLOOK_OFF_2007_BLUE 215
#define ID_VIEW_APPLOOK_OFF_2007_BLACK 216
#define ID_VIEW_APPLOOK_OFF_2007_SILVER 217
#define ID_VIEW_APPLOOK_OFF_2007_AQUA 218
I have used what the Wizard uses upon the creation of a new application. Nevertheless, any other number that is not in use will do it. Now make the initialization for what I was talking about:
This is in fact a request from the application to acquire the key from the registry with the string of ApplicationLook. If it does not exist (first time run), use the second argument. Declare the function that will take care of the rest in the Mainframe class:
afx_msg void OnApplicationLook(UINT id);
Create the definition for it:
void CMainFrame::OnApplicationLook(UINT id)
{
CWaitCursor wait;
theApp.m_nAppLook = id; // set the id of the new style
switch (theApp.m_nAppLook) //set the corresponding style
Once this is done, just call it from the OnCreate function of the Mainframe class. This way, upon the creation of the window, the new style will be applied.
OnApplicationLook(theApp.m_nAppLook);
There you have it. The result looks quite good if you ask me. Here you have the source code:
With this, I have come to the end of this article. Come back next time to see how we turn our application into a tab-based multi-windows version, and how to extend it with a ribbon. Thank you for reading my article up to this point; please share your comments and ideas below. Please feel free to rate the article as well. Live With Passion!