Using MFC to make installation program -- the use of Wizard dialog box

reference resources: Chicken pecks rice
When we install the software, a dialog box will pop up to tell us the steps. This is the Wizard dialog box. Today, let's study the method of creating Wizard dialog box.
The two basic classes of the Wizard dialog box are property page class CMFCPropertyPage and property sheet class CMFCPropertySheet. Each property page is equivalent to a special dialog box, and the property sheet is the integration of these dialog boxes, that is, the main dialog box. Next, let's simulate an installer.

1, Create resource

Create a dialog based MFC program called Guide. The framework of VS automatic generation is based on CDialogEx. We need to use CMFCPropertySheet, so first combine the automatically generated dialog resources with guidedlg h GuideDlg. CPP delete.
Now let's start to create dialog resources for several property pages, and you can place controls at will. I created three property pages, as shown in the figure:


It should be noted here that we do not need to add buttons such as "previous" and "next" on the property page. These buttons are encapsulated in the property sheet class.
Next, we set the border of each property page to Thin, the style to Child, the title bar to False, and modify the ID to IDD respectively_ Synopsis IDD_CONFIRM IDD_INSTALL.

2, Create property page class

Right click to add a class for each property page. The class name is optional, but be sure to note that the base class should be modified to CMFCPropertyPage!!!

After creating the property page class, we open the source file and find that there is something wrong with the automatically generated constructor:
Originally, there are only these constructors of CMFCPropertyPage class:

// Construction
public:
	CMFCPropertyPage();
	CMFCPropertyPage(UINT nIDTemplate, UINT nIDCaption = 0);
	CMFCPropertyPage(LPCTSTR lpszTemplateName, UINT nIDCaption = 0);

Obviously, we need to use the second one to create a property page through the resource ID, so we delete the pParent after each property page constructor:

CConfirmPage::CConfirmPage(CWnd* pParent /*=nullptr*/)
	: CMFCPropertyPage(IDD_CONFIRM)
{

}

Each property page has its own specific functions and specific buttons. For example, the first page cannot have "previous step", and there cannot be "previous step" or "next step" after installation. Because each property page is different, we cannot set it in the property sheet class, but only in the property page class. MFC provides many virtual functions for us to overload:

  1. CMFCPropertyPage::OnSetActive
    The description of this function is: called when this page becomes the active page (i.e. displayed). That is, this is an initialization function. We can modify the displayed buttons here.
  2. CMFCPropertyPage::OnWizardNext/Back/Finish
    The description of this function is: when using the Wizard dialog box, it is called when you click Next / previous / finish. Here we can deal with the required behavior after pressing the button.

Introduction property page

Let's start with the first property page (Introduction). This property page does not require any additional operation. We just need to set the "next" button in the OnSetActive function.
Note: it cannot be set in OnInitDialog, because the process of switching property pages is actually to show and hide the window without re creation. Therefore, OnInitDialog function is only called once, which can not meet the requirements.
CMFCPropertySheet::SetWizardButtons
To enable or disable Back, Next or Finish buttons on the Wizard dialog box, you should call this function before calling DoModal. The function prototype is:
void SetWizardButtons (
DWORD dwFlags
);
Parameter dwFlags: sets the appearance and function properties of wizard buttons. Can be a combination of the following values: PSWIZB_BACK enables the back button, which is disabled if this value is not included.
PSWIZB_NEXT enables the "next" button and disables the "next" button if this value is not included.
PSWIZB_FINISH enables the finish button.
PSWIZB_DISABLEDFINISH displays the disabled Finish button.

We can use this function to display the "next" button.

BOOL CSynopsisPage::OnSetActive()
{
	// Get the parent window, that is, the property sheet CMFCPropertySheet class
	CMFCPropertySheet* psheet = (CMFCPropertySheet*)GetParent();
	// There is only the next button for setting the property sheet   
	psheet->SetWizardButtons(PSWIZB_NEXT);

	return CMFCPropertyPage::OnSetActive();
}

Confirm installation property page

The second property page (confirmation) property page is a little complicated. This property page needs to display the "back" and "next" buttons, and a confirmation dialog box will pop up when you click "next".
We can set the button in the OnSetActive function.

BOOL CConfirmPage::OnSetActive()
{
	// Get the parent window, that is, the property sheet CMFCPropertySheet class
	CMFCPropertySheet* psheet = (CMFCPropertySheet*)GetParent();
	// There are "back" and "next" buttons for setting the property sheet   
	psheet->SetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT);

	return CMFCPropertyPage::OnSetActive();
}

To pop up a dialog box for confirmation, we can overload the CMFCPropertyPage::OnWizardNext function. But the key problem is that this function is only an interface for additional code, and the code for switching property pages is not in it. Therefore, our operation in this function will not affect the switching property pages, which means that if we cancel the installation and return directly, we will still switch to the next page! Fortunately, MFC provides a function to simulate button pressing:
CMFCPropertySheet::PressButton
Simulate pressing a specified button. The function prototype is:
void PressButton(
int nButton
);
Parameter nButton: to simulate the pressed button, it can be one of the following values:
PSBTN_BACK select the "back" button.
PSBTN_NEXT select the "next" button.
PSBTN_FINISH selects the "finish" button.
PSBTN_OK select the "OK" button.
PSBTN_APPLYNOW select the "Apply" button.
PSBTN_CANCEL select the "Cancel" button.
PSBTN_HELP select the help button.
In this way, if the user chooses to cancel, we can simulate pressing the "back" button to cancel.

LRESULT CConfirmPage::OnWizardNext()
{
	//Pop up dialog box OK
	LRESULT result = CMFCPropertyPage::OnWizardNext();
	if (MessageBoxW(L"Are you sure to install?", L"installation wizard", MB_ICONQUESTION | MB_YESNO) == IDNO)
		((CMFCPropertySheet*)GetParent())->PressButton(PSBTN_BACK);
	return result;
}

Installation property page

Since we only simulate the installation, this property page does not need to perform the real installation operation, but only simulates the completion status of the installation, hides the "previous", "next" and "Cancel" buttons, and displays "complete".
We can set it through SetWizardButtons, but there is a simpler way
SetFinishText
Set the text of the finish button in the wizard property sheet, show and enable the button, and hide the next and previous buttons. The function prototype is:
void SetFinishText(
LPCTSTR lpszText
);
Parameter lpszText: long pointer to a null terminated string containing the new text of the finish button.
Using this function can also modify text, so let's try this function.

BOOL CInstallPage::OnSetActive()
{
	// Get the parent window, that is, the property sheet CMFCPropertySheet class   
	CMFCPropertySheet* psheet = (CMFCPropertySheet*)GetParent();
	//Setting the property sheet has only the finish button 
	psheet->GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);//Hide cancel button
	psheet->SetFinishText(L"Complete installation");

	return CMFCPropertyPage::OnSetActive();
}

So far, the property page has been created.

3, Create property sheet class

We click item - class wizard in the menu. After the class wizard dialog box pops up, click the drop-down button of add class, select MFC class, add a class named CGuideSheet, and select CMFCPropertySheet as the base class.

In cguidesheet H contains three property page header files, and then add instances of each property page class as private members.

#pragma once
#include "CSynopsisPage.h"
#include "CConfirmPage.h"
#include "CInstallPage.h"
class CGuideSheet :
    public CMFCPropertySheet
{
public:
	CGuideSheet(LPCTSTR pszCaption, CWnd* pParentWnd = nullptr, UINT iSelectPage = 0);
private:
	CSynopsisPage m_page0;
	CConfirmPage m_page1;
	CInstallPage m_page2;
};

In the constructor, we will add three property pages to the property sheet.
CMFCPropertySheet::AddPage
Add a new property page to the properties dialog box. The function prototype is:
void AddPage(
CPropertyPage *pPage
);
Parameter pPage: the object pointer of the new property page to be added.

CGuideSheet::CGuideSheet(LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
	: CMFCPropertySheet(pszCaption, pParentWnd, iSelectPage)
{
	AddPage(&m_page0);
	AddPage(&m_page1);
	AddPage(&m_page2);
}

In this way, the complete property sheet class is created.

4, Modification and perfection

Now we have created the Wizard dialog box, but there is another problem: how to display it? We open the guide CPP, you can find the code of the original pop-up dialog box in the middle of CGuideApp::InitInstance function:

	CGuideDlg dlg; 
	m_pMainWnd = &dlg;
	INT_PTR nResponse = dlg.DoModal();

We just need to change the code here to show the Wizard dialog box. The code is as follows:

	CGuideSheet sheet(L"Setup Wizard"); 
	sheet.SetWizardMode();
	m_pMainWnd = &sheet;
	INT_PTR nResponse = sheet.DoModal();

We ran the program and found the following two problems:

  1. The title of the Wizard dialog box is not displayed normally;
  2. There is a annoying help button below.

For these two problems, we can solve them by modifying the OnInitDialog function of CGuideSheet.
First set the title with SetTitle, and then hide it by getting the handle of the help button. (optional)

BOOL CGuideSheet::OnInitDialog()
{
	BOOL bResult = CMFCPropertySheet::OnInitDialog();
	SetTitle(m_strCaption);
	GetDlgItem(IDHELP)->ShowWindow(SW_HIDE);
	return bResult;
}

5, Operation results




Keywords: C++ MFC

Added by py343 on Wed, 02 Feb 2022 23:56:31 +0200