【3D技术宅公社】XR数字艺术论坛  XR技术讨论 XR互动电影 定格动画

 找回密码
 立即注册

QQ登录

只需一步,快速开始

调查问卷
论坛即将给大家带来全新的技术服务,面向三围图形学、游戏、动画的全新服务论坛升级为UTF8版本后,中文用户名和用户密码中有中文的都无法登陆,请发邮件到324007255(at)QQ.com联系手动修改密码

3D技术论坛将以计算机图形学为核心,面向教育 推出国内的三维教育引擎该项目在持续研发当中,感谢大家的关注。

查看: 3614|回复: 1

处理控件通知消息-vc++编程指南

[复制链接]
发表于 2006-1-13 11:09:29 | 显示全部楼层 |阅读模式
<>5.3.6处理控件通知消息 <BR><BR>  虽然Register已经可以登录数据了,但读者会很快会发现该程序还有一些不完善的地方: <BR><BR>  登录完一个人的数据后,对话框就关闭了,若用户有很多人的数据要输入,则必须频繁地打开对话框,很不方便。在登录数据时,应该使对话框一直处于打开状态。 <BR><BR>  登录数据对话框分个人情况和单位情况两组,若被调查人是下岗职工,则不必输入单位情况。程序应该能够对用户的输入及时地作出反应,即当用户选择了“下岗”单选按钮时,应使单位情况组中的控件禁止。一个禁止的控件呈灰色示,并且不能接收用户的输入。 <BR><BR>  要解决上述问题,就必须对控件通知消息进行处理。当控件的状态因为输入等原因而发生变化时,控件会向其父窗口发出控件通知消息。例如,如果用户在登录数据对话框中的某一按钮(包括普通按钮、检查框和单选按钮)上单击鼠标,则该按钮会向对话框发送BN_CLICKED消息。对话框根据按钮的ID激活相应的BN_CLICKED消息处理函数,以对单击按钮这一事件作出反应。通过对按钮的BN_CLICKED消息的处理,我们可以使登录数据对话框的功能达到上述要求。 <BR><BR>  首先,让我们来解决第一个问题。我们的设想是修改原来的“确定(Y)”按钮,使得当用户点击该按钮后,将数据输出到视图中,并且对话框不关闭,以便用户输入下一个数据。请读者按下面几步进行修改。 <BR><BR>  修改登录数据对话框的“确定(Y)”按钮,使该按钮的标题变为“添加(&amp;A)”,ID变为IDC_ADD。这样,当用户点击该按钮后,对话框会收到BN_CLICKED消息。由于这个BN_CLICKED消息对应的按钮ID不是IDOK,不会触发OnOK消息处理函数,因此不会关闭对话框。 <BR><BR>  为按钮IDC_ADD的BN_CLICKED消息创建消息处理函数。创建的方法是进入ClassWizard后,选Message Maps页并在Class name栏中选择CRegisterDialog,然后在Object IDs栏中选择IDC_ADD,在Messages栏中双击BN_CLICKED。在确认使用缺省的消息处理函数名OnAdd后,按回车键退出ClassWizard。 <BR><BR>  OnAdd要向编辑视图输出正文,就必须获得一个指向CRegisterView对象的指针以访问该对象。为此,请在CRegisterDialog类的说明中加入下面一行<BR>Cwnd* m_pParent;<BR>注意不要加在AFX注释对中。 <BR><BR>  为实现IDC_ADD按钮的功能,请按清单5.5和清单5.6修改程序。主要的改动是把原来由CRegiserView::OnEditRegister完成的在视图中输出数据的任务交给CRegisterDialog::OnAdd来完成。 <BR><BR> 清单5.5 CRegisterView::OnEditRegister函数 <BR></P>
<> void CRegisterView::OnEditRegister()  <BR><BR> { <BR><BR> // TODO: Add your command handler code here <BR><BR> CRegisterDialog dlg(this); <BR><BR> dlg.DoModal(); <BR><BR> } </P>
<><BR> 清单5.6 CRegisterDialog类的部分源代码 <BR></P>
<> CRegisterDialog::CRegisterDialog(CWnd* pParent /*=NULL*/) <BR><BR> : CDialog(CRegisterDialog::IDD, pParent) <BR><BR> { <BR><BR> //{{AFX_DATA_INIT(CRegisterDialog) <BR><BR> . . . . . . <BR><BR> //}}AFX_DATA_INIT <BR><BR> m_pParent=pParent; <BR><BR> } <BR><BR> void CRegisterDialog::OnAdd()  <BR><BR> { <BR><BR> // TODO: Add your control notification handler code here <BR><BR> //更新数据 <BR><BR> UpdateData(TRUE); <BR><BR> //检查数据是否有效 <BR><BR> if(m_strName=="" || m_nSex&lt;0 || m_nWork&lt;0 || m_strUnit==""  <BR><BR> || m_strKind=="" || m_strIncome=="") <BR><BR> { <BR><BR> AfxMessageBox("请输入有效数据"); <BR><BR> return; <BR><BR> } <BR><BR> CString str; <BR><BR> //获取编辑正文 <BR><BR> m_pParent-&gt;GetWindowText(str); <BR><BR> //换行 <BR><BR> str+="\r\n"; <BR><BR> str+="姓名:"; <BR><BR> str+=m_strName; <BR><BR> str+="\r\n"; <BR><BR> str+="性别:"; <BR><BR> str+=m_nSex?"女":"男"; <BR><BR> str+="\r\n"; <BR><BR> str+="年龄:"; <BR><BR> CString str1; <BR><BR> //将数据格式输出到字符串对象中 <BR><BR> str1.Format("%d",m_nAge); <BR><BR> str+=str1; <BR><BR> str+="\r\n"; <BR><BR> str+="婚否:"; <BR><BR> str+=m_bMarried?"已婚":"未婚"; <BR><BR> str+="\r\n"; <BR><BR> str+="就业状况:"; <BR><BR> str+=m_nWork?"下岗":"在职"; <BR><BR> str+="\r\n"; <BR><BR> str+="工作单位:"; <BR><BR> str+=m_strUnit; <BR><BR> str+="\r\n"; <BR><BR> str+="单位性质:"; <BR><BR> str+=m_strKind; <BR><BR> str+="\r\n"; <BR><BR> str+="工资收入:"; <BR><BR> str+=m_strIncome; <BR><BR> str+="\r\n"; <BR><BR> //更新编辑视图中的正文 <BR><BR> m_pParent-&gt;SetWindowText(str); <BR><BR> } </P>
<><BR>  CRegisterDialog的构造函数有一个参数pParent,该参数是一个指向CWnd对象的指针,用于指定对话框的父窗口或拥有者窗口。在CRegisterView:: OnEditRegister函数中,在构建CRegisterDialog对象时指定了this参数,this指针指向CRegisterView对象本身。这样在调用CRegisterDialog的构造函数时,this指针值被赋给了CRegisterDialog的成员m_pParent。OnAdd函数可利用m_pParent来访问对话框的拥有者即CRegisterView对象。 <BR><BR> 提示:术语父窗口(Parent)是相对于子窗口而言。若某一个窗口拥有一个子窗口(Child),则该窗口就被称为子窗口的父窗口。子窗口就是具有WS_CHILD风格的窗口,子窗口依赖于父窗口且完全被限制在父窗口内部。拥有者窗口(owner)相对于被拥有者窗口而言。若某一个窗口拥有一个非子窗口,则该窗口被称为拥有者窗口。被拥有窗口(owned)不具有WS_CHILD风格,可在屏幕上任意移动。  <BR><BR>  当用户用鼠标点击IDC_ADD按钮时,该按钮的BN_CLICKED消息处理函数CRegisterDialog::OnAdd将被调用。在OnAdd中,首先调用了UpdateData(TRUE)以把数据从控件传给对话框的数据成员变量。然后,程序要对数据的有效性进行检查,如果输入的数据不完全有效,则会显示一个消息对话框,提示用户输入有效的数据。接下来进行的工作是在视图中输出数据,这部分代码与清单5.4类似,读者应该比较熟悉了。 <BR><BR>  完成上述工作后,登录数据对话框就变得较为实用了。打开对话框后,用户可以方便地输入多人的数据,只有按了取消按钮后,对话框才会关闭。 <BR><BR>  接下来让我们来解决第二个问题。解决该问题的关键在于当用户点击“在职”或“下岗”单选按钮时,程序要对收到的BN_CLICKED消息作出响应。有些读者可能会想到为两个单选按钮分别创建BN_CLICKED消息处理函数,这在只有两个单选按钮的情况下是可以的,但如果一组内有多个单选按钮,则分别创建消息处理函数就比较麻烦了。利用MFC提供的消息映射宏ON_CONTROL_RANGE可以避免这种麻烦,该映射宏把多个ID连续的控件发出的消息映射到同一个处理函数上。这样,我们只要编写一个消息处理函数,就可以对“在职”和“下岗”两个单选按钮的BN_CLICKED消息作出响应。ClassWizard不支持ON_CONTROL_RANGE宏,所以我们必须手工创建单选按钮的消息映射和消息处理函数。 <BR><BR>  首先,在CRegisterDialog类的头文件中加入消息处理函数的声明,该函数名为OnWorkClicked,如清单5.7所示。 <BR><BR> 清单5.7 BN_CLICKED消息处理函数OnWorkClicked的声明 <BR><BR> . . . . . .  <BR><BR> protected: <BR><BR> void OnWorkClicked(UINT nCmdID); <BR><BR> // Generated message map functions <BR><BR> //{{AFX_MSG(CRegisterDialog) <BR><BR> virtual BOOL OnInitDialog(); <BR><BR> afx_msg void OnAdd(); <BR><BR> //}}AFX_MSG <BR><BR> . . . . . . <BR><BR>  然后,在CRegisterDialog类的消息映射中加入ON_CONTROL_RANGE映射,如清单5.8所示。ON_CONTROL_RANGE映射的形式是ON_CONTROL_RANGE <BR></P>
 楼主| 发表于 2006-1-13 11:09:53 | 显示全部楼层
清单5.8 在CRegisterDialog类的消息映射中加入ON_CONTROL_RANGE映射 <BR><BR> BEGIN_MESSAGE_MAP(CRegisterDialog, CDialog) <BR><BR> //{{AFX_MSG_MAP(CRegisterDialog) <BR><BR> ON_BN_CLICKED(IDC_ADD, OnAdd) <BR><BR> //}}AFX_MSG_MAP <BR><BR> ON_CONTROL_RANGE(BN_CLICKED, IDC_WORK, IDC_WORK1, OnWorkClicked) <BR><BR> END_MESSAGE_MAP() <BR><BR>  ON_CONTROL_RANGE消息映射宏的第一个参数是控件消息码,第二和第三个参数分别指明了一组连续的控件ID中的头一个和最后一个ID,最后一个参数是消息处理函数名。如果读者是按表5.2的顺序放置控件的则IDC_WORK和IDC_WORK1应该是连续的。这样,无论用户是在IDC_WORK还是在IDC_WORK1单选按钮上单击,都会调用OnWorkClicked消息处理函数。 <BR><BR>  提示:如果不能确定两个ID是否是连续的,请用File-&gt;Open命令打开resource.h文件,在该文件中有对控件ID值的定义。如果发现两个ID是不连续的,读者可以改变对ID的定义值使之连续,但要注意改动后的值不要与别的ID值发生冲突。 <BR><BR>  最后,在CRegisterDialog类所在CPP文件的最后插入消息处理函数CRegisterDialog::OnWorkClicked,如清单5.9所示。 <BR><BR>  清单5.9 CRegisterDialog::OnWorkClicked消息处理函数 <BR><BR> void CRegisterDialog::OnWorkClicked(UINT nCmdID)  <BR><BR> { <BR><BR> //判断“在职”单选按钮是否被选中 <BR><BR> if(IsDlgButtonChecked(IDC_WORK)) <BR><BR> { <BR><BR> //使控件允许 <BR><BR> GetDlgItem(IDC_UNIT)-&gt;EnableWindow(TRUE); <BR><BR> GetDlgItem(IDC_KIND)-&gt;EnableWindow(TRUE); <BR><BR> GetDlgItem(IDC_INCOME)-&gt;EnableWindow(TRUE); <BR><BR> } <BR><BR> else <BR><BR> { <BR><BR> //清除编辑框的内容并使之禁止 <BR><BR> GetDlgItem(IDC_UNIT)-&gt;SetWindowText(""); <BR><BR> GetDlgItem(IDC_UNIT)-&gt;EnableWindow(FALSE); <BR><BR> //使组合框处于未选择状态并使之禁止 <BR><BR> CComboBox *pComboBox=(CComboBox *)GetDlgItem(IDC_KIND); <BR><BR> pComboBox-&gt;SetCurSel(-1); <BR><BR> pComboBox-&gt;EnableWindow(FALSE); <BR><BR> //使列表框处于未选择状态并使之禁止 <BR><BR> m_ctrlIncome.SetCurSel(-1); <BR><BR> m_ctrlIncome.EnableWindow(FALSE); <BR><BR> } <BR><BR> } <BR><BR>  OnWorkClicked函数判断“在职”单选按钮是否被选中。若该按钮被选中,则使单位情况组中的控件允许,若该按钮未被选中,则说明“下岗”按钮被选中,这时应使控件禁止,清除编辑框中的正文, 并且使组合框和列表框处于未选中状态。 <BR><BR>  在OnWorkClicked函数中主要调用了下列函数: <BR><BR>  CWnd::IsDlgButtonChecked函数,用来判断单选按钮或检查框是否被选择,该函数的声明为<BR>UINT IsDlgButtonChecked(int nIDButton) const;<BR>参数nIDButton为按钮的ID。若按钮被选择,则函数返回1,否则返回0,若按钮处于不确定状态,则返回值为2。 <BR><BR>  CWnd::GetDlgItem函数,用来获得指向某一控件的指针,该函数的声明为<BR>CWnd* GetDlgItem(int nID) const;<BR>参数nID为控件的ID。该函数返回一个指定控件的CWnd对象指针,通过该指针,程序可以对控件进行控制。 <BR><BR>  CWnd::EnableWindow函数,该函数使窗口允许或禁止,禁止的窗口呈灰色显示,不能接收键盘和鼠标的输入。该函数的声明是<BR>BOOL EnableWindow( BOOL bEnable = TRUE );<BR>若参数bEnable的值为TRUE,则窗口被允许,若bEnable的值为FALSE,则窗口被禁止。 <BR><BR>  CListBox::SetCurSel和CComboBox::SetCurSel函数功能类似,用来使列表中的某一项被选中,选中的项呈高亮度显示。函数的声明是<BR>int SetCurSel(int nSelect);<BR>参数nSelect指定了新选项的索引,第一项的索引值为0,若nSelect的值为-1,那么函数将清除以前的选择,使列表处于未选择状态。 <BR><BR>  有时,需要将GetDlgItem返回的CWnd指针强制转换成控件对象的指针,以便调用控件对象专有的成员函数对控件进行控制。例如,在程序中GetDlgItem(IDC_KIND)返回的指针被强制转换成CComboBox类型,只有这样,才能调用CComboBox::SetCurSel成员函数。 <BR><BR>  为了对控件进行查询和控制,在程序中采用了两种访问控件的方法。一种方法是直接利用ClassWizard提供的控件对象,例如m_ctrlIncome列表框对象。另一种方法是利用CWnd类提供的一组管理对话框控件的成员函数,例如程序中用到的GetDlgItem和IsDlgButtonChecked。这两种方法是在对话框内访问控件的常用方法,读者都应该掌握。表5.5列出了管理对话框控件的Cwnd成员函数。 <BR><BR>   <BR><BR> 表5.5 用来管理对话框控件的CWnd成员函数 <BR><BR> 函数名  <BR> 功能  <BR><BR> CheckDlgButton  <BR> 选中或不选中按钮控件。  <BR><BR> CheckRadioButton  <BR> 选择一个指定的单选按钮并使同组内的其它单选按钮不被选择。  <BR><BR> DlgDirList  <BR> 往一个列表框中添加文件、目录或驱动器的列表。  <BR><BR> DlgDirListComboBox  <BR> 往一个组合框中的列表框内添加文件、目录或驱动器的列表。  <BR><BR> DlgDirSelect  <BR> 从一个列表框中获得当前选择的文件、目录或驱动器。  <BR><BR> DlgDirSelectBomboBox  <BR> 从一个组合框中获得当前选择的文件、目录或驱动器。  <BR><BR> GetCheckedRadioButton  <BR> 返回指定的单选按钮组中被选择的单选按钮的ID。  <BR><BR> GetDlgItem  <BR> 返回一个指向一给定的控件的临时对象的指针。  <BR><BR> GetDlgItemInt  <BR> 返回在一个指定的控件中由正文表示的数字值。  <BR><BR> GetDlgItemText  <BR> 获得在一个控件内显示的正文。  <BR><BR> GetNextDlgGroupItem  <BR> 返回一个指向一组控件内的下一个或上一个控件的临时对象的指针。  <BR><BR> GetNextDlgTabItem  <BR> 返回下一个tab顺序的控件的临时对象的指针。  <BR><BR> IsDlgButtonChecked  <BR> 返回一个按钮控件的状态。  <BR><BR> SendDlgItemMessage  <BR> 把一个消息传送给一个控件。  <BR><BR> SetDlgItemInt  <BR> 将一个整数转换为正文,并将此正文赋给控件。  <BR><BR> SetDlgItemText  <BR> 设置一个控件显示的正文。  <BR><BR><BR> 编译并运行Register看看,现在的登录数据对话框已经比较令人满意了。 <BR>
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|3D数字艺术论坛 ( 沪ICP备14023054号 )

GMT+8, 2025-2-6 04:33

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表