The Base Class
The keys to the design are a few interface methods I added to the base class (along with a factory method I will discuss in an upcoming section). The interface methods are Setup(), DoOK(), and DoCancel() (see Listing 1). They are called for certain events, like so:
// allow derived class to carry out "OK" action
// allow derived class to carry out "Cancel" action
The derived classes override Setup(), DoOK(), and DoCancel(), so the events effectively get passed down. You can expand upon this idea in your own code, adding or subtracting virtual methods to control the flow of the program. I kept the message handlers for the above events in the base class for this design concept, since I often find I want to add common action code to them.
A Breakdown of the Elements
|Figure 4. Starting Out: Entering some new text. That's all the user can do here. No printing is allowed, so no Print button. If the user re-enters this screen, the text buffer will be a clean slate again.|
The CTextDlg class cannot be instantiated by itself, which makes CreateDlg() a key element. CreateDlg() is a factory method that takes an enumerated value (from dlgType), instantiates one of the CTextDlg-derived classes based on that value, and returns the object as a base pointer (CTextDlg *) (see Listing 2
). The main dialog code (which I will get to later) uses this method to create the custom screens.
The application-specific code for this sample is embodied in several elements, declared in CTextDlg.h: SetText(), GetText(), m_Text, m_EditCtrl, and m_PrintBtnCtrl. I use SetText() and GetText() in the main dialog code to get/set the text displayed in the edit control of the common dialog. Note that I declared SetText() as pure virtual, because the derived classes need to control when and where the text buffer (CTextDlg::m_Text) gets initialized. To clarify, m_Text is bound to the edit control, which enables me to set the control's text by assigning a string to it.
The variables m_EditCtrl and m_PrintBtnCtrl represent the edit control and the Print button, respectively. The subclasses use them both to modify the user's access to them. Class Wizard places such variables in the dialog class that corresponds to the resource where they are located, CTextDlg. I figure where they are located doesn't make much of a difference, and moving them to the subclass level seems more work than necessary since code in DoDataExchange() would need to be moved down as well.
|Figure 5. Changing Things A Little: The original message has been modified. Again, no printing allowed from here, so no Print button.|
Each of the screens needs to deal only with what makes it unique. As you will see, the screens don't require very much code.
The New Text screen is the simplest. It always starts out with an empty text buffer and then saves whatever the user types. Notice that although SetText() exists in the code, it doesn't do anything (see Listings 3 and 4). This is one of the prices you pay for using an interface: it must be fully implemented even if not totally practical. The best thing to do is either leave the impractical portions of the implementation empty or make them illegal (raise an error). It's a judgment call.
The New Text screen is simply the common dialog box without the Print button showing (see Figure 4).
The Edit Text screen looks just like the New Text screen (see Figure 5). The implementation is also similar. In this case, however, SetText() does something useful. The main dialog code calls it so the user can see what was entered previously. CEditTextDlg allows the user to edit the text and then saves it (see Listings 5 and 6).