Creating a Visual C++ Wizard for Single-Window MFC Applications

Nigel Thompson

April 30, 1997

Click to copy the files in the ONEWNDWZ sample application for this technical article.

Introduction

This article explains how to remove the MFC document/view architecture from an MFC AppWizard project and replace it with a single-window design. Then, using parts of that code for the template, I will show you how to create a Microsoft Visual C++® 5.0 wizard that generates single-window application frameworks quickly and easily.

Removing the MFC Document/View Architecture

The standard set of application wizards built into the Visual C++ development system allows you to create a variety of applications based on the document/view architecture, but what if that’s not what you need? Your only alternative “out of the box” is to create a dialog-based application, and that’s what most folks settle for if they want to create a simple single-window application quickly.

One alternative to using the dialog-based application wizard is to use the regular MFC-based application wizard to create a Single Document Interface (SDI) application, which includes support for the document/view architecture, and then remove the files that support document/view. This is how I usually create my own single-window applications, and it’s from this approach that I created the single-window wizard we’ll be looking at later. Let’s see how we go about converting an SDI application created by AppWizard to a single-window framework. Let’s assume the application is called TEST:

  1. From the File menu, select New and use the MFC AppWizard (exe) option to create a new project named TEST. Select the Single Document option with no database support, no compound document support, and whatever toolbar, status bar, and printing support you want.

  2. Remove the document and view source (.cpp) and header (.h) files from the project. To do this, delete these four files from the project directory: TESTdoc.cpp, TESTdoc.h, TESTview.cpp, and TESTview.h.

  3. Edit TEST.cpp to remove all references to TESTdoc.h and TESTview.h.

  4. Now edit the InitInstance function in the main source file. In this case, we will edit CTestApp::InitInstance in TEST.cpp. Replace all the code that initializes the document/view code with a small piece of code that creates the main window. The final function should look like this:
    BOOL CTestApp::InitInstance()
    {
       AfxEnableControlContainer();
    
       // Standard initialization
       // If you are not using these features and wish to reduce the size
       // of your final executable, you should remove from the following
       // the specific initialization routines you do not need.
    
    #ifdef _AFXDLL
       Enable3dControls();         // Call this when using MFC in a shared DLL.
    #else
       Enable3dControlsStatic();   // Call this when linking to MFC statically.
    #endif
    
       // Change the registry key under which our settings are stored.
       // You should modify this string to be something appropriate,
       // such as the name of your company or organization.
       SetRegistryKey(_T(“Local AppWizard-Generated Applications”));
    
       LoadStdProfileSettings();  // Load standard INI file options (including MRU).
    
       // Load the main frame window.
       CMainFrame* pFrame = new CMainFrame;
       if (!pFrame->LoadFrame(IDR_MAINFRAME, WS_OVERLAPPEDWINDOW | WS_VISIBLE)) {
          return FALSE;
       }
                        
       // Save the main window pointer.
       m_pMainWnd = pFrame;
    
       return TRUE;
    }
    
  5. Edit mainframe.h and make the CMainFrame constructor public. The start of the class declaration should look like this:
    class CMainFrame : public CFrameWnd
    {
    public:
       CMainFrame();
       DECLARE_DYNCREATE(CMainFrame)
       [. . .]
    
  6. Rebuild all the dependencies (from the Build menu, select Update All Dependencies) and build the application to confirm that you have edited all the code correctly.

  7. Run the application, which shows a single window.

Now you can work with the CMainFrame window and do anything that you would normally do with a simple single-window application.

Coping with Tool and Status Bars

Once you’ve got the single window framework up, you’ll quite likely want to create some child windows within the client area of the CMainFrame window. In order to do this successfully, you will need to manage the tool and status bars that share space with your proposed child.

Let’s say that we have created a new window class named CMyWindow and we want to use one of these to fill the client area of the CMainFrame window. We add a CMyWindow object to the protected data section of the CMainFrame class declaration in mainframe.h, as follows:

   CMyWindow m_MyWnd;

Add the following code to the end of CMainFrame::OnCreate to create the child window:

   #define MY_CHILD_ID 1

   // Create the child window.
   m_MyWnd.Create(NULL,
               “”,
              WS_CHILD | WS_VISIBLE,
              CRect(0, 0, 0, 0),
              this,
              MY_CHILD_ID, // Child window ID
              NULL);

Now use the ClassWizard to add a RecalcLayout function to CMainFrame. Edit the code so that it looks like this:

void CMainFrame::RecalcLayout(BOOL bNotify) 
{
   CFrameWnd::RecalcLayout(bNotify);

   // Find the space that’s left over.
   CRect rc;
   RepositionBars(0,
              0xFFFF,
              MY_CHILD_ID, // Child window ID
              CWnd::reposQuery,
              &rc);
   if (IsWindow(m_MyWnd.GetSafeHwnd())) {
      m_MyWnd.MoveWindow(&rc, FALSE);
   }
}

Now, when the CMainFrame window is resized, the control bars will be moved into new positions and your child window will be resized to fit into the remaining space. Note that when you create the child window you need to assign it an ID value, and it is this ID value that’s used to identify your child window in the call to the RepositionBars function.

A Wizard to Create Single-Window Applications

I had resisted attempting to create my own wizards for Visual C++ for some time because I always felt that the process of creating a wizard was far more complex than the effort it took to create my own application frameworks. However, after creating a single-window framework for the umpteenth time, I decided that a wizard might well be worth the work after all.

You can create wizards in several ways. The simplest is to create one based on a framework that an AppWizard has generated. Then you can either alter the options slightly, or you can do your own thing for a full-blown custom design. I figured the pain would be worth it and decided I wanted a custom design for my wizard.

The process of creating a custom application wizard is well documented in the Visual C++ documents, so I’ll just briefly cover the order of events and then show you some of the details of the files I ended up with in my own wizard. That should be enough for you to do your own thing. Just one warning—my wizard is trivial beyond belief. Your own design might well require a little more work!

  1. Use the Custom AppWizard to create a new project. I named mine OneWndWz, and I elected to use my own custom steps and to have just one step initially.

  2. Build the framework to ensure it’s okay before you start altering it.

  3. Copy all the source files from the application that you want the wizard to create to the wizard project’s template directory. (For example, I might copy test.cpp, test.h, mainframe.cpp, mainframe.h, and so on. Don’t forget that all the resource files go into the same directory—not a res subdirectory.)

  4. Edit the newproj.inf file so that it will copy the files for the newly generated application framework to the correct places. This is well documented in Visual C++. My own file looks like this:
    $$// newproj.inf = template for list of template files
    $$//  format is ‘sourceResName’ \t ‘destFileName’
    $$//    The source res name may be preceded by any combination of
    $$//    ‘=’, ‘-’,  ‘!’, ‘?’, ‘:’, ‘#’, and/or ‘*’
    $$//       ‘=’ => The resource is binary.
    $$//       ‘-’ => The file should not be added to the project.
    $$//       ‘!’ => The file should be marked exclude from build.
    $$//       ‘?’ => The file should be treated as a help file.
    $$//       ‘:’ => The file should be treated as a resource.
    $$//       ‘#’ => The file should be treated as a template (implies ‘!’).
    $$//       ‘*’ => Bypass the custom AppWizard resources when loading.
    $$//   If name starts with / =>, then create a new subdir.
    
    mainfrm.cpp   mainfrm.cpp
    mainfrm.h   mainfrm.h
    resource.h   resource.h
    stdafx.cpp   stdafx.cpp
    stdafx.h   stdafx.h
    tbasic.cpp   $$root$$.cpp
    tbasic.h   $$root$$.h
    tbasic.rc   $$root$$.rc
    /res
    =tbasic.ico   res\$$root$$.ico
    tbasic.rc2   res\$$root$$.rc2
    
  5. Add the files as template resources. Note that you need to be sure to import .ico files and other binary files as custom rather than auto. For each one imported, set its ID value to its name in quotes, for example “mainfrm.cpp”.

  6. Edit the source files and replace project-specific text with macros. In the single-window wizard case, I simply replace the application name with the $$root$$ macro. So, for example, part of the mainframe.cpp file looks like this:
    // MainFrm.cpp : implementation of the CMainFrame class
    
    #include “stdafx.h”
    #include “$$root$$.h”
    
    #include “MainFrm.h”
    [. . .]
    
  7. Edit the custom dialog template. OneWndWz requires very little here because it has no options.

  8. Edit the confirm.inf file, which gives the user details about the project he or she is creating. My file contains only the following two lines of text:
    The wizard will create a project named $$root$$.
    

    There are no options.

  9. Edit the Help file and provide whatever help you think the user might need.

If you add options to your wizard, you’ll need to use more macros and some of the conditional statements that are explained in the Visual C++ documentation.

Installation

During development of a wizard, Visual C++ automatically copies the wizard files to the right place for you. If you need to install a wizard manually, you must copy the .awx and .hlp files to the SharedIDE\Template directory of your Visual C++ installation. The full default path to the directory is C:\Program Files\DevStudio\SharedIDE\Template.

Summary

Removing the MFC document/view architecture to leave a single-window framework is fairly easy to do, requiring only the removal of some files and a few code alterations. Creating a custom wizard is a bit more work, but will make it trivial to create as many single-window applications as you might need.

As usual, you can contact me with questions about this or any other topic by e-mail at nigel-t@msn.com. You can also read some of my other work on my Web site at http://ourworld.compuserve.com/homepages/nigelt/.