使用MFC制作安装程序——向导对话框的使用

参考:鸡啄米
我们平常安装软件时,都会弹出一个对话框告诉我们步骤,这就是向导对话框。今天,我们就来研究一下创建向导对话框的方法。
向导对话框最基本的两个类是属性页类CMFCPropertyPage和属性表类CMFCPropertySheet。每个属性页都相当于一个特殊的对话框,而属性表是这些对话框的集成,也就是主对话框。下面,我们就来模拟一个安装程序。

一、创建资源

新建一个基于对话框的MFC程序,名为Guide。VS自动生成的框架是基于CDialogEx的,我们要用CMFCPropertySheet,所以先把自动生成的对话框资源和GuideDlg.h GuideDlg.cpp删除。
现在,我们开始创建几个属性页的对话框资源,可以任意放置控件。我创建了3个属性页,如图所示:
使用MFC制作安装程序——向导对话框的使用_第1张图片
使用MFC制作安装程序——向导对话框的使用_第2张图片
使用MFC制作安装程序——向导对话框的使用_第3张图片

这里要注意,我们不需要在属性页上添加“上一步”“下一步”等按钮,这些按钮都被封装在了属性表类里。
接着,我们把每个属性页的边框都设为Thin,样式设为Child,标题栏设为False,并修改ID分别为IDD_SYNOPSIS(简介)IDD_CONFIRM(确认)IDD_INSTALL(安装)。
使用MFC制作安装程序——向导对话框的使用_第4张图片

二、创建属性页类

我们分别为每一个属性页右击添加类,类名随意,但一定要注意,基类要修改为CMFCPropertyPage!!!
使用MFC制作安装程序——向导对话框的使用_第5张图片
创建属性页类后,我们打开源文件,发现自动生成的构造函数有点问题:使用MFC制作安装程序——向导对话框的使用_第6张图片
原来,CMFCPropertyPage类的构造函数只有这几个:

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

显然,我们需要使用第二个,通过资源ID创建属性页,所以我们分别把每一个属性页构造函数后面的pParent删掉:

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

}

每一个属性页都有自己特定的功能和特定的按钮,如第一页不能有“上一步”,安装完成后不能有“上一步”“下一步”等等。因为每个属性页都不同,所以我们不能在属性表类里设置,只能在属性页类里设置。MFC对此提供了很多虚函数供我们重载:

  1. CMFCPropertyPage::OnSetActive
    这个函数的说明是:当此页成为活动页(即被显示)时调用。也就是说,这是个初始化函数。我们可以在这里修改显示的按钮。
  2. CMFCPropertyPage::OnWizardNext/Back/Finish
    这个函数的说明是:当使用向导对话框时,在单击“下一步”/“上一步”/“完成”时调用。我们可以在这里处理按下按钮后需要的行为。

简介属性页

我们首先来看第一个属性页(简介)。这个属性页不需要任何额外操作,我们只要在OnSetActive函数中设置显示“下一步”按钮就行了。
注:不能在OnInitDialog中设置,因为我们切换属性页的过程其实是在显示和隐藏窗口,并没有重新创建,所以OnInitDialog函数只调用一次,不能满足需求。
CMFCPropertySheet::SetWizardButtons
在向导对话框上启用或禁用Back、Next或Finish按钮,应在调用DoModal之前调用此函数。函数原型为:
void SetWizardButtons (
DWORD dwFlags
);
参数dwFlags:设置向导按钮的外观和功能属性。可以是以下值的组合: PSWIZB_BACK 启用“Back”按钮,如果不包含此值则禁用“Back”按钮。
PSWIZB_NEXT 启用“Next”按钮,如果不包含此值则禁用“Next”按钮。
PSWIZB_FINISH 启用“Finish”按钮。
PSWIZB_DISABLEDFINISH 显示禁用的“Finish”按钮。

我们可以用这个函数来显示“下一步”按钮。

BOOL CSynopsisPage::OnSetActive()
{
	// 获得父窗口,即属性表CMFCPropertySheet类
	CMFCPropertySheet* psheet = (CMFCPropertySheet*)GetParent();
	// 设置属性表只有“下一步”按钮   
	psheet->SetWizardButtons(PSWIZB_NEXT);

	return CMFCPropertyPage::OnSetActive();
}

确认安装属性页

第二个属性页(确认)属性页就有些复杂了。这个属性页需要显示“上一步”和“下一步”按钮,还要在单击“下一步”时弹出对话框确认。
我们可以在OnSetActive函数设置按钮。

BOOL CConfirmPage::OnSetActive()
{
	// 获得父窗口,即属性表CMFCPropertySheet类
	CMFCPropertySheet* psheet = (CMFCPropertySheet*)GetParent();
	// 设置属性表有“上一步”和“下一步”按钮   
	psheet->SetWizardButtons(PSWIZB_BACK | PSWIZB_NEXT);

	return CMFCPropertyPage::OnSetActive();
}

要想弹出对话框确认,我们可以重载CMFCPropertyPage::OnWizardNext函数。但关键问题是,这个函数只是一个附加代码的接口,切换属性页的代码并不在里面,所以,我们在这个函数里的操作并不会影响切换属性页,这意味着如果我们取消安装,直接return,依然会切换到下一页!幸好,MFC提供了一个模拟按钮按下的函数:
CMFCPropertySheet::PressButton
模拟按下某指定的按钮。函数原型为:
void PressButton(
int nButton
);
参数nButton:要模拟按下的按钮,它可以是下列值之一:
PSBTN_BACK 选择“Back”按钮。
PSBTN_NEXT 选择“Next”按钮。
PSBTN_FINISH 选择“Finish”按钮。
PSBTN_OK 选择“OK”按钮。
PSBTN_APPLYNOW 选择“Apply”按钮。
PSBTN_CANCEL 选择“Cancel”按钮。
PSBTN_HELP 选择“帮助”按钮。
这样,如果用户选择了取消,我们可以模拟按下“上一步”按钮,就可以抵消了。

LRESULT CConfirmPage::OnWizardNext()
{
	//弹出对话框确定
	LRESULT result = CMFCPropertyPage::OnWizardNext();
	if (MessageBoxW(L"确定安装吗?", L"安装向导", MB_ICONQUESTION | MB_YESNO) == IDNO)
		((CMFCPropertySheet*)GetParent())->PressButton(PSBTN_BACK);
	return result;
}

安装属性页

由于我们只是模拟安装,所以这一个属性页不需要执行真正的安装操作,只是模拟安装完成的状态,隐藏“上一步”“下一步”和“取消”按钮,显示“完成”。
我们可以通过SetWizardButtons设置,但还有一种更简单的方法
SetFinishText
在向导属性表中设置“完成”按钮的文本,显示并启用该按钮,并隐藏“下一步”和“上一步”按钮。函数原型为:
void SetFinishText(
LPCTSTR lpszText
);
参数lpszText:指向包含“完成”按钮新文本的以空字符结尾的字符串的长指针。
使用这个函数还能修改文本,所以我们使用这个函数试试。

BOOL CInstallPage::OnSetActive()
{
	// 获得父窗口,即属性表CMFCPropertySheet类   
	CMFCPropertySheet* psheet = (CMFCPropertySheet*)GetParent();
	//设置属性表只有“完成”按钮 
	psheet->GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);//隐藏“取消”按钮
	psheet->SetFinishText(L"完成安装");

	return CMFCPropertyPage::OnSetActive();
}

到此为止,属性页创建完毕。

三、创建属性表类

我们点击菜单中的项目-类向导,弹出类向导对话框后,点击添加类的下拉按钮,选择MFC类,添加名为CGuideSheet的类,基类选择为CMFCPropertySheet。
使用MFC制作安装程序——向导对话框的使用_第7张图片
在CGuideSheet.h中包含三个属性页头文件,然后分别添加每个属性页类的实例作为私有成员。

#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;
};

在构造函数中,我们要把三个属性页添加到属性表中。
CMFCPropertySheet::AddPage
为属性对话框添加新的属性页。函数原型为:
void AddPage(
CPropertyPage *pPage
);
参数pPage:要添加的新的属性页的对象指针。

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

这样,完整的属性表类就创建完毕了。

四、修改与完善

现在我们已经创建完向导对话框,但还有一个问题:怎么显示它呢?我们打开Guide.cpp,在CGuideApp::InitInstance函数的中间可以找到原来弹出对话框的代码:

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

我们只需要把这里改为显示向导对话框的代码就可以了。代码如下:

	CGuideSheet sheet(L"安装向导程序"); 
	sheet.SetWizardMode();
	m_pMainWnd = &sheet;
	INT_PTR nResponse = sheet.DoModal();

我们运行程序,发现有以下两个问题:

  1. 向导对话框标题未正常显示;
  2. 下面有一个很烦人的“帮助”按钮。

对于这两个问题,我们可以通过修改CGuideSheet的OnInitDialog函数解决。
首先使用SetTitle设置标题,再通过获取“帮助”按钮句柄的方式把它隐藏掉。(可选)

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

五、运行结果

使用MFC制作安装程序——向导对话框的使用_第8张图片
使用MFC制作安装程序——向导对话框的使用_第9张图片
使用MFC制作安装程序——向导对话框的使用_第10张图片
使用MFC制作安装程序——向导对话框的使用_第11张图片

你可能感兴趣的:(mfc,c++)