Professional Documents
Culture Documents
Custom MFC Base Classes
Custom MFC Base Classes
The original article for this PDF can be found on DevCentral. http://devcentral.iticentral.com
by David Campbell
Comment on this article
The Problem
Somewhere today in a cubicle, the following scenario is playing out. An unsuspecting Windows developer has just completed the fiftieth dialog class in a project, and the phone rings. Marketing has called to tell our fellow developer that Legal has decided that all of his dialogs must have disclaimer text in every dialog caption. In my latest latest personal encounter with this situation, a "slight code change" needed to be added so third-party help systems could execute correctly. This code change had to be added to every CDialog-based class. Before resigning myself to a long day of hacking dialog code (if lucky), I took a walk and thought about another option.
The Solution
The good news is, there is a solution. The bad news is, it won't help someone in the above situation avoid making changes to every dialog class. The first time, all of the dialogs will have to be modified, but only slightly. However, after this first set of changes, should the dialogs ever need to be tweaked again, you'll need to make only a single change. If implemented when at the start of the project, however, you'll avoid ever needing to touch all of the dialogs. The solution requires simply putting a piece of code between our dialogs and the MFC CDialog class. This is very easy to do, and provides you with a way to make a single code-change propogate through each dialog in your project.
The Example
To demonstrate the new class, I built a new project called "DevJournal" using the Visual Studio Application Wizard. I selected Dialog-based, and allowed the remainder of the options to remain at their default values. Incidentally, I am using Visual Studio 6.0. I will try my best to not use things that are 6.0-specific, but it may happen. If you have trouble in version 5.0, write me, and I will try to address the issues. My normal working method is to build and execute small pieces to avoid not knowing which change made what break. Accordingly, once I have the DevJournal project set up in Visual Studio, I build it, both Debug and Release. Execution displays a single dialog with OK and Cancel buttons, and the text "TODO: Place dialog controls here."
Now rebuild and run the application to make sure the About box appears on cue.
This is because we are going to pass in the IDD to our parent, and we keep the defaulted pParent variable to the right of our UINT. Remove the enum line from the AFX_DATA section. Each CDJDialog-based class will have its own enum, and pass that value in as the IDD in the constructor. For now, that is enough for the include file. Now we modify the DJDialog.cpp file as follows. Modify the constructor from CDJDialog::CDJDialog(CWnd* pParent /*=NULL*/) : CDialog(CDJDialog::IDD, pParent)
This passes the IDD that our new class was instantiated with to the CDialog base class in the same manner that it receives the member enum IDD. Rebuild at this point to ensure a clean compile, and no changes in the manner of execution.
to CAboutDlg::CAboutDlg() : CDJDialog(CAboutDlg::IDD)
to BEGIN_MESSAGE_MAP(CAboutDlg, CDJDialog)
Modify the CDevJournalDlg constructor from CDevJournalDlg::CDevJournalDlg(CWnd* pParent /*=NULL*/) : CDialog(CDevJournalDlg::IDD, pParent)
As with the CAbout class, above, modify the BEGIN_MESSAGE_MAP entry from BEGIN_MESSAGE_MAP(CDevJournalDlg, CDialog)
to BEGIN_MESSAGE_MAP(CDevJournalDlg, CDJDialog)
This is all that is necessary to base our dialogs on our new class. Build and run to ensure completeness at this point. Don't yawn yet, because if it runs as it did before that means your subclass is working along with everyone else!
This won't do anything, unless we also modify our two CDJDialog-based classes. In CDevJournalDlg.cpp , modify the base-class call in OnInitDialog() from CDialog::OnInitDialog();
to CDJDialog::OnInitDialog();
The CAbout class does not currently handle the WM_INITDIALOG method. In a manner similar to modifying CDJDialog, add a WM_INITDIALOG handler to the CAbout class. Since this code is added after our base class substitution, Class Wizard will automatically be aware of CDJDialog, and no changes to that part of the code are required. Build and run to test. Because of the AfxMessageBox placement, the message appears prior to either dialog appearing. This is a good test of the execution of our base class. At this point, the hard work is finished. Any functionality you would like to have appear in every dialog class can now be added to a single class, and will immediately work in every dialog in your project.
Build and run the application to check your results. You will notice that the About dialog box picked up the background while the main dialog did not. A little investigation will show why. If you open the class view of Visual Studio, you'll notice that CAbout does not handle the OnPaint method. Therefore that task is passed on to the base class, CDJDialog, and we just wrote the code to do that task.
Conclusion
Once you understand the concepts, the actual work of doing this should go fairly quickly. I have considered adding a custom CDialog-based class to every future project for just this sort of use.
2001 Interface Technologies, Inc. All Rights Reserved Questions or Comments? devcentral@iticentral.com PRIVACY POLICY