Nigel Thompson
April 30, 1997
Click to copy the files in the ONEWNDWZ sample application for this technical article.
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.
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:
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;
}
class CMainFrame : public CFrameWnd
{
public:
CMainFrame();
DECLARE_DYNCREATE(CMainFrame)
[. . .]
Now you can work with the CMainFrame window and do anything that you would normally do with a simple single-window application.
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.
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!
$$// 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
// MainFrm.cpp : implementation of the CMainFrame class
#include “stdafx.h”
#include “$$root$$.h”
#include “MainFrm.h”
[. . .]
The wizard will create a project named $$root$$.
There are no options.
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.
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.
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/.