8.2.3向导的创建

创建一个向导类型的对话框,应该遵循创建一个属性表单的步骤来实现,但是在调用属性表单对象之前的DoModal函数之前,应该先调用SetWizardMode函数。
propSheet.SetWizardMode();
运行程序,如图。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

但是第一页不应该有上一步的按钮,最后一页不应该有下一步按钮,而应该是完成的按钮。
CPropertySheet类提供了一个SetWizardButtons成员函数,用来设置向导对话框上面的按钮。
void SetWizardButtons( DWORD dwFlags );
参数PSWIZB_BACK设置一个上一步按钮。
参数PSWIZB_NEXT设置一个下一步按钮。
参数PSWIZB_FINISH设置一个完成按钮。
参数PSWIZB_DISABLEDFINISH设置一个禁用的完成按钮。

应该在属性页的OnSetActive函数中调用SetWizardButtons函数。OnSetActive函数是一个虚函数,因此需要在属性页子类中重写这个函数,然后根据需要设置该属性页上面的按钮。
CProp类上单击鼠标右键->Add Virtue Function->选中OnSetActive->Add and Edit。
然后再重写的OnSetActive函数中,调用属性表单对象的SetWizardButtons函数设置第一个属性页上面的按钮。因为属性表单是属性页的父窗口,所以,通过GetParent可以获得属性表单的指针;但是还需要用强制类型转换,将CWnd类型的指针转换成CPropertySheet类型。

BOOL CProp1::OnSetActive() 
{
 // TODO: Add your specialized code here and/or call the base class
 ((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB_NEXT);
 return CPropertyPage::OnSetActive();
}

同样地为第二个属性页设置上一步和下一步按钮。

BOOL CProp2::OnSetActive() 
{
 // TODO: Add your specialized code here and/or call the base class
 ((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB_NEXT|PSWIZB_BACK);
 return CPropertyPage::OnSetActive();
}

为第三个属性页设置上一步和完成按钮。

BOOL CProp3::OnSetActive() 
{
 // TODO: Add your specialized code here and/or call the base class
 ((CPropertySheet*)GetParent())->SetWizardButtons(PSWIZB_FINISH|PSWIZB_BACK);
 return CPropertyPage::OnSetActive();
}

处理第一个页面:对每个页面进行判断,检查用户是否做出选择,如果没有,就禁止程序进入下一个界面。
首先为了操作方便,为这个页面上面的单选按钮关联一个成员变量:
在第一个单选按钮上面单击鼠标右键->ClassWizard->Cancel
选择Member Variable选项卡,但是没有看到单选按钮的ID,这是因为对于一组单选按钮来说,需要将一组单选按钮的第一个按钮属性的Group选项勾选上。
选中IDC_RADIO1->Add Variable,并且为控件关联的变量名为:m_occupation,变量类型只能选择int,如图。然后在CProp类头文件中看到该变量的定义,在该类的构造函数中也将该变量初始化为-1。
在这里插入图片描述

CProp1::CProp1() : CPropertyPage(CProp1::IDD)
{
 //{{AFX_DATA_INIT(CProp1)
 m_occupation = -1;
 //}}AFX_DATA_INIT
}

当为第一个按钮选中了Group选项后,随后两个单选按钮就跟这个按钮是一组了,直到遇到下一个具有Group属性的控件为止。
当勾选第一个按钮时,它所关联的成员变量m_occupation=0,当勾选第二个按钮时,它所关联的成员变量m_occupation=1,当勾选第三个按钮时,它所关联的成员变量m_occupation=2,一个也没选中就为-1。
另外CProp1中DoDataExchange函数中,添加了一条DDX_Radio函数的调用,用来在单选按钮控件与成员变量之间交换数据。

void CProp1::DoDataExchange(CDataExchange* pDX)
{
 CPropertyPage::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CProp1)
 DDX_Radio(pDX, IDC_RADIO1, m_occupation);
 //}}AFX_DATA_MAP
}

当用户单击属性页的下一步按钮时,程序会调用OnWizardNext虚函数,如果这个函数返回0,程序会自动进入下一个属性页;如果返回-1,就禁止属性页发生改动。
重写这个虚函数,并且在这个虚函数中添加以下代码:

LRESULT CProp1::OnWizardNext() 
{
 // TODO: Add your specialized code here and/or call the base class
 if(m_occupation==-1)
 {
  MessageBox("请选择你的职业!");
  return -1;
 }
 return CPropertyPage::OnWizardNext();
}

运行,选中一个,但是单击下一步发现会弹出对话框,如图。
在这里插入图片描述

原因就是成员变量没有从控件哪里得到值,如果CProp1类的OnWizardNext函数要从控件哪里得到关联的变量的值,就应该以TRUE为参数调用UpdataData函数,这个参数默认值就是TRUE。

LRESULT CProp1::OnWizardNext() 
{
 // TODO: Add your specialized code here and/or call the base class
 UpdateData();
 if(m_occupation==-1)
 {
  MessageBox("请选择你的职业!");
  return -1;
 }
 return CPropertyPage::OnWizardNext();
}

运行,发现就能正常运行了。如图
在这里插入图片描述

接下来为工作地点的列表框中增加一些工作地点,在属性页显示之前添加:
也就是在WM_INITDIALOG消息的函数中完成这一任务,因此为CProp1累加WM_INITDIALOG消息的响应函数。
对控件的操作都是通过MFC的类完成的,对于列表框,也有一个与之对应的MFC类:CListBox,该类提供了成员函数AddString,用来向列表框添加字符串。

BOOL CProp1::OnInitDialog() 
{
 CPropertyPage::OnInitDialog(); 
 // TODO: Add extra initialization here
 ((CListBox*)GetDlgItem(IDC_LIST1))->AddString("北京");
 ((CListBox*)GetDlgItem(IDC_LIST1))->AddString("上海");
 ((CListBox*)GetDlgItem(IDC_LIST1))->AddString("湖北");
 return TRUE;  // return TRUE unless you set the focus to a control
               // EXCEPTION: OCX Property Pages should return FALSE
}

运行结果如图。
在这里插入图片描述

用户必须选择一个工作地点,才能进入下一页。与前面单选按钮一样,首先得给这个列表框控件关联一个成员变量,设置成员变量名为m_workAddr,发现这时成员变量类型只能为CString。
同时在CProp1类的构造函数中对m_workAddr变量进行了初始化:
m_workAddr=_T("");
并且在DoDataExchange函数中自动添加了以下代码,实现列表框控件与成员变量之间的数据交换:
DDX_LBString(pDX, IDC_LIST1, m_workAddr);
在OnWizardNext函数中,对列表框所关联的成员变量进行判断,看列表框有没有选中一个工作地点:

LRESULT CProp1::OnWizardNext() 
{
 // TODO: Add your specialized code here and/or call the base class
 UpdateData();
 if(m_occupation==-1)
 {
  MessageBox("请选择你的职业!");
  return -1;
 }
 if(m_workAddr=="")
 {
  MessageBox("请选择你的工作地点!");
  return -1;
 }
 return CPropertyPage::OnWizardNext();
}

处理第二个页面:
因为以上是四个复选框,所以可以多选,所以为四个变量分别关联一个成员变量。如图。
在这里插入图片描述

Prop2类的构造函数中,将新添加的四个成员变量都初始化为false。

CProp2::CProp2() : CPropertyPage(CProp2::IDD)
{
 //{{AFX_DATA_INIT(CProp2)
 m_football = FALSE;
 m_basketball = FALSE;
 m_volleyball = FALSE;
 m_swim = FALSE;
 //}}AFX_DATA_INIT
}

现在我们希望实现,如果没有选择任何一个兴趣爱好,就不让程序进入下一个属性页。
为Prop2程序重写OnWizardNext函数。

LRESULT CProp2::OnWizardNext() 
{
 // TODO: Add your specialized code here and/or call the base class
 UpdateData();
 if(m_football||m_basketball||m_swim||m_volleyball)
 return CPropertyPage::OnWizardNext();
 else
 {
  MessageBox("请选择你的兴趣爱好!");
  return -1;
 }
}

处理第三个页面
组合框控件是一个编辑框和列表框组成,对应的MFC类是CComboBox,该类也有一个成员函数:AddString,用来向组合框中的列表框添加字符串选项。
首先为CProp3类添加WM_INITDIALOG消息添加消息相应函数,在该消息响应函数中添加列表框中的选项。

BOOL CProp3::OnInitDialog() 
{
 CPropertyPage::OnInitDialog();
 
 // TODO: Add extra initialization here
 ((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString("1000元以下");
 ((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString("1000-2000元");
 ((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString("2000-3000元");
 ((CComboBox*)GetDlgItem(IDC_COMBO1))->AddString("3000元以上");
 return TRUE;  // return TRUE unless you set the focus to a control
               // EXCEPTION: OCX Property Pages should return FALSE
}

运行结果如图。如果组合框选项的顺序与输入顺序一样,就在属性框Style中的Sort勾选去掉。
在这里插入图片描述

另外,如果希望在第三个对话框初始显示时,编辑框中有一个初始选择的项,这个时候可以通过一个组合框成员函数:SetCurSel函数来实现
((CComboBox*)GetDlgItem(IDC_COMBO1))->SetCurSel(0);
在列表框中索引为0的作为默认的选项,那么该程序就不用判断用户是否进行了选择。

接收用户在向导中所做的选择。将用户的选择输出到视类的窗口中。
首先为第三个页面添加一个CString类型的成员变量:m_strSalary。因为后面其他类要访问它,设置为公有的。用来接收用户的选择,当用户单击完后按钮时,就将选的薪资水平保存到该变量中。
给CProp3类添加一个虚函数:OnWizardFinish,以处理完成按钮的单击消息。
首先得获得当前选中项的索引值,用CComboBox类GetCurSel函数;然后利用CComboBox类GetLBText函数,获得指定位置的一个字符串。
void GetLBText( int nIndex, CString& rString ) const;
第一个参数就是索引位置,第二个参数是接收字符串的缓存。

BOOL CProp3::OnWizardFinish() 
{
 // TODO: Add your specialized code here and/or call the base class
 int index;
 index=((CComboBox*)GetDlgItem(IDC_COMBO1))->GetCurSel();
 ((CComboBox*)GetDlgItem(IDC_COMBO1))->GetLBText(index,m_strSalary);
 return CPropertyPage::OnWizardFinish();
}

为了接收用户在向导中的选择,在视类中需要定义些变量来保存他们,并将他们的访问权限设置为私有。
第一个页面 职业 int m_iOccupation
第一个页面 工作地点 CString m_strWorkAddr
第二个页面 爱好 BOOL m_bLike[4]
第三个页面 薪资水平 CString m_strSalary
并且在视类的构造函数中初始化这些变量:

CPropView::CPropView()
{
 // TODO: add construction code here
 m_iOccupation=-1;
 m_strWorkAddr="";
 memset(m_bLike,0,sizeof(m_bLike));
 m_strSalary="";
}

memset将m_bLike数组进行初始化为0。

一般情况下,CPropertySheet类的DoModal函数的返回值是IDOK或IDCANCEL。但是如果属性表单已经被创建为向导了,那么函数的返回值将是ID_WIZFINISH或IDCANCEL。
如果返回的是完成按钮的ID_WIZFINISH,才进行输出处理。
DoModal函数返回后,属性表单窗口就被销毁了,但是属性表单对象的生命周期并没有结束。

void CPropView::OnPropertysheet() 
{
 // TODO: Add your command handler code here
 CPropSheet propSheet("属性表单");
 propSheet.SetWizardMode();
 if(ID_WIZFINISH==propSheet.DoModal())
 {
  m_iOccupation=propSheet.m_prop1.m_occupation;
  m_strWorkAddr=propSheet.m_prop1.m_workAddr;
  m_bLike[0]=propSheet.m_prop2.m_football;
  m_bLike[1]=propSheet.m_prop2.m_basketball;
  m_bLike[2]=propSheet.m_prop2.m_volleyball;
  m_bLike[3]=propSheet.m_prop2.m_swim;
  m_strSalary=propSheet.m_prop3.m_strSalary;
  Invalidate();
 }
 propSheet.DoModal();
}

获取到用户的选择后,就可以在视类窗口中显示它们。
因为调用了Invalidate函数,所以在视类OnDraw函数中完成信息的输出。

void CPropView::OnDraw(CDC* pDC)
{
 CPropDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here
 CFont font;
 font.CreatePointFont(300,"华文行楷");
 CFont *pOldFont;
 pOldFont=pDC->SelectObject(&font);

CString strTemp;
 strTemp="你的职业:";

switch(m_iOccupation)
 {
 case 0:
  strTemp+="程序员";
  break;
 case 1:
  strTemp+="系统工程师";
  break;
 case 2:
  strTemp+="项目经理";
  break;
 default:
  break;
 }
 pDC->TextOut(0,0,strTemp);

strTemp="你的工作地点:";
 strTemp+=m_strWorkAddr;
 TEXTMETRIC tm;
 pDC->GetTextMetrics(&tm);
 pDC->TextOut(0,tm.tmHeight,strTemp);

strTemp="你的兴趣爱好:";
 if(m_bLike[0])
 {
  strTemp+="足球 ";
 }
 if(m_bLike[1])
 {
  strTemp+="篮球 ";
 }
 if(m_bLike[0])
 {
  strTemp+="排球 ";
 }
 if(m_bLike[0])
 {
  strTemp+="游泳 ";
 }
 pDC->TextOut(0,tm.tmHeight*2,strTemp);

strTemp="你的薪资水平:";
 strTemp+=m_strSalary;
 pDC->TextOut(0,tm.tmHeight*3,strTemp);

pDC->SelectObject(pOldFont);
}

各个爱好文本之间以空格隔开。
运行,初始状态如图。
在这里插入图片描述

选入相应选项后,视类展现的界面如图。
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

身影王座

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值