VC++:对话框
《VC++深入详解:第七章》 孙鑫
在MFC中,对资源的操作通常都是通过一个与资源相关的类来完成的。
DoDataExchange:主要用来完成对话框数据的交换和校验。
对话框的创建
动态创建按钮
CWnd对象都有一个成员变量m_hWnd,用来保存与窗口对象相关联的窗口句柄,如果窗口对象没有与任何一个窗口相关联,此句柄为NULL。
atoi——将char[]转换为int,用法:num1 = atoi(ch1);
itoa——将数值转换为文本,用法:itoa(num3,ch3,10);
CWnd类还提供了一个成员函数:GetDlgItemText,这个函数将返回对话框中指定ID的控件上的文本,即组合了GetDlgItem和GetWindowText两个函数的功能;同样对应的也有SetDlgItemText。
CWnd类还有另一对成员函数:GetDlgItemInt和SetDlgItemInt。GetDlgItemInt函数返回指定控件的文本,并将其转换为一个整形数值。
在DoDataExchange函数内部实现了对话框控件与类成员变量的关联。MFC提供了多种以DDX_为前缀的函数,这些函数分别用于不同控件的数据交换;MFC也提供了多个以DDV_为前缀的数据校验函数。
- DoDataExchange
- DDX——Dialog Data Exchange,对话框数据交换;
- DDV——Dialog Data Validation,对话框数据校验。
Windows程序都是基于消息的系统,因此,为了获取或设置窗口的文本,只要知道获取或设置窗口文本的消息,就可以通过SendMessage来发送这条消息,从而获取或设置窗口的文本。
Windows系统中,获取窗口文本的消息是WM_GETEXT,发送该消息后,系统将把指定窗口的文本复制到调用者提供的一个缓存中;设置窗口文本的消息是WM_SETTEXT。
- WM_GETTEXT消息与WM_SETTEXT消息的附加信息
- WM_GETTEXT
- wParam指定将复制的字符数
- lParam就是调用者提供的用来保存窗口文本的缓存地址
- WM_SETTEXT
- wParam没使用,值为0
- lParam参数指定了用来设置窗口文本的字符串地址
- SendMessage消息说明
- Platform SDK和CWnd类都提供SendMessage函数;所以,如果想要调用Platform SDK的函数,前面必须加双冒号(::)
- 每个窗口类对象都有一个保存了窗口句柄的成员m_hWnd。获取编辑框控件窗口句柄的方法:可以通过调用GetDlgItem函数获取编辑框窗口对应的C++对象指针,然后通过该指针获得窗口句柄。
对话框的创建
- 模态对话框的创建
- DoModal
- 代码
- CTestDlg dlg;
- dlg.DoModal();
- 非模态对话框的创建
- Create
- 注意:创建非模态对话框时,不能把对话框对象定义为局部对象。
- 将对话框对象定义为视类的成员变量
- 将它定义为指针,在堆上分配内存(在堆上分配的内存与程序的整个生命周期是一致的)
- 错误代码(dlg是局部对象、无法显示)
- CTestDlg dlg;
- dlg.Create(IDD_DIALOG1,this);
- dlg.ShowWindow(SW_SHOW);
- 正确代码
- CTestDlg *pDlg = new CTestDlg;
- pDlg->Create(IDD_DIALOG1,this);
- pDlg->ShowWindow(SW_SHOW);
- 解决内存丢失问题
- 方法1:将这个指针变量定义为视类的成员变量,然后在视类的析构函数中调用delete;
- 方法2:在CTestDlg中重载PostNcDestory虚函数,释放this指针所指向的内存
- void CTestDlg::PostNcDestroy()
- {
- delete this;
- CDialog::PostNcDestroy();
- }
动态创建按钮
想法:为对话框创建一个CButton类的对象m_btn?
- 做法
- 先为CTestDlg类添加一个私有的CButton成员变量m_btn
- 之后,m_btn.Create("New",BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD, CRect(0,0,100,100),this,123);
- 解决多次创建按钮非法操作问题
- 为CTestDlg类增加一个私有的BOOL成员变量m_bIsCreated(标识是否已经创建过按钮窗口了),之后调用Create函数时对变量进行判断
- 在OnBtnAdd函数中将m_bIsCreated设置为static的(static BOOL bIsCreated = FALSE;)
- 直接判断与窗口关联的句柄m_hWnd,代码
- void CTestDlg::OnBtnAdd()
- {
- if(!m_btn.m_hWnd)
- {
- m_btn.Create( "New",BS_DEFPUSHBUTTON|WS_VISIBLE|WS_CHILD, CRect(0,0,100,100),this,123);
- }
- else
- {
- m_btn.DestroyWindow();
- }
- }
静态文本控件
- 操作静态文本控件
- 获取静态文本框控件
- 函数:CWnd *GetDlgItem(int nID) const;
- 获取静态文本控件的文本
- 函数:GetWindowText
- 设置静态文本控件的文本
- 函数:SetWindowText
- 代码
- CString str;
- if(GetDlgItem(IDC_NUMBER1)->GetWindowText(str), str == "Number1:")
- {
- GetDlgItem(IDC_NUMBER1)->SetWindowText("数值1:");
- }
- else
- {
- GetDlgItem(IDC_NUMBER1)->SetWindowText("Number1:");
- }
- 注意:静态文本控件在默认状态下不发送通告消息,需要在其属性对话框中选择Notify选项
编辑框控件
- 求和:第三个编辑框显示前两个编辑框的和。
- 七种实现方式(即:七种访问对话框控件的方式)
- 用CWnd的GetDlgItem和GetWindowText/SetWindowText、itoa/atoi
- 用GetDlgItemText/SetDlgItemText、itoa/atoi
- 用GetDlgItemInt/SetDlgItemInt
- 将三个编辑框分别与对话框类的三个成员变量相关联,然后通过这些成员变量来检索和设置编辑框的文本
- 添加值变量:int型,Value
- 使用UpdateData(FALSE)更新显示;UpdataData(TRUE)更新数据。
- 说明:添加值变量之后程序的变化
- 类的头文件,两个AFX_DATA注释宏之间增加了成员变量int m_num1;
- CTestDlg类构造函数中增加了初始化代码m_num1 = 0;
- CTestDlg类的DoDataExchange函数内部调用了三个DDX_Text函数,类似DDX_Text(pDX,IDC_EDIT1,m_num1);
- 这种方式可以设置输入框数值范围。设置范围后程序变化:DoDataExchange函数中增加了下述代码:
- DDV_MinMaxInt(pDX,m_num1,0,100);
- DDV_MinMaxInt(pDX,m_num2,0,100);
- 将编辑框控件关联控件变量
- 添加控件变量,Control, CEdit
- 添加控件变量之后程序的变化
- 类的头文件,连个AFX_DATA注释宏之间增加了成员变量CEdit m_edit1;
- DoDataExchange函数中增加了三个DDX_Control函数,分别将一个对话框控件与一个控件变量相关联,类似DDX_Control(pDX,IDC_EDIT1,m_edit1);
- 说明:这些控件变量代表的就是控件本身,并且CEdit类派生于CWnd类,因此,可以利用这些控件变量调用GetWindowText和SetWindowText来获取和设置编辑框的文本。
- 通过SendMessage发送消息,从而获取或设置窗口的文本
- ::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch1);
- ::SengMessage(m_edit2.m_hWnd,WM_GETTEXT,10,(LPARAM)ch2);
- m_edit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3);
- 通过直接给对话框的子控件发送消息来完成对控件的访问
- 函数:SendDlgItemMessage,相当于GetDlgItem和SendMessage的结合
- SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1);
- SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch2);
- SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,0,(LPARAM)ch3);
- 7种方式总结:
- 获取和设置编辑框复选的内容
- 前提:如果要对编辑框控件的内容进行复选,当前焦点应该位于这个编辑框上
- 可以利用EM_GETSEL消息获得编辑框复选的内容
- 这个消息的wParam参数将接收复选内容的开始位置
- lParam参数将接收复选内容的结束位置
- 这两个参数都要求是指向DWORD类型的指针
- EM_SETSEL用来设置编辑框控件中的复选内容
- 例如:SendDlgItemMessage(IDC_EDIT3,EM_SETSEL,1,3);
- 注意:为了看到第三个编辑框上的复选内容,需要把焦点转移到这个编辑框上,使用m_edit3.SetFocus();
- 注意2:如果这个消息wParam参数值为0,lParam参数值为-1,则所有内容将被复选
- 说明:EM_开头的消息是指编辑框控件消息(Edit Control Message)
对话框伸缩功能
- 几个重要函数及用法示例
- rectLarge.IsRectNull(); //矩形几个坐标值是否都为零
- rectLarge.IsRectEmpty(); //矩形区域是否为空
- GetWindowRect(&rectLarge); //获得当前窗口矩形
- SetWindowPos(NULL,smallRect.left,smallRect.top,smallRect.Width(),smallRect.Height(),SWP_NOMOVE | SWP_NOZORDER); //设置窗口的位置和大小
- 实现
- 定义两个矩形变量:rectLarge/rectSmall(静态变量)
- 判断对话框的原始尺寸是否已经赋值
- IsRectEmpty——检测矩形区域是否为空,若宽度和高度为0或负值,说明矩形为空
- IsRectNull——如果矩形左上角和右下角四个坐标值都是0,返回非零值;否则返回0
- 有了原始和切除后的矩形尺寸,利用SetWindowsPos函数来设置对话框的收缩和扩展之后的大小
- SetWindowPos函数
- 作用:设置窗口的位置和大小
- pWndInsertAfter:标识一个CWnd对象,该对象是在以Z次序排序的窗口中位于当前窗口前面的那个窗口对象;该对象可以是指向某个CWnd对象的指针,也可以是下面几个取值之一:
- wndBottom
- wndTop
- wndTopMost
- wndNoTopMost
- x,y:左上角xy坐标
- cx,xy:窗口宽度和高度
- nFlags:设定窗口的尺寸和定位
- 代码示例
输入焦点的传递
- 主要内容
- SetWindowLong():改变指定窗口的属性
- OnInitDialog():替换之前的WM_INITDIALOG消息,在对话框及其子控件创建完成、显示之前执行
- 自定义窗口过程捕获回车字符
- if (uMsg == WM_CHAR && wParam == 0x0d)
- 设置焦点至下一个控件
- ::SetFocus(GetNextWindow(hwnd, GW_HWNDNEXT));
- SetFocus用来设置控件的焦点,参数为控件的句柄(HWND)
- 实现的功能
- 在测试对话框中第一个编辑框中按下回车键后,输入焦点被转移到第二个编辑框。
- 思路:可以通过捕获键盘按键消息,然后在此消息响应函数中把输入焦点移动到下一个编辑框控件来实现。
- 两种实现方式
- 为编辑框控件生成一个相关联的类,然后利用这个类来捕获键盘按键消息。
- 修改编辑框控件的窗口过程函数,即自己编写一个编辑框控件的窗口过程函数,然后替换MFC提供的默认的编辑框控件窗口过程函数。
- SetWindowLong函数
- 改变指定窗口的属性。当一个窗口已经创建之后,修改该窗口已制定的过程函数可以通过此函数来实现。
- 参数取值及含义
- hWnd:指定想要改变其属性的窗口句柄
- nIndex:指定要设置的偏移地址
- 取值:
- GWL_EXSIYLE:设置一个新的扩展窗口风格
- GWL_STYLE:设置一个新的窗口风格
- GWL_WNDPROC:设置一个新的窗口过程
- GWL_HINSTANCE:设置一个新的应用程序实例句柄
- GWL_ID:为窗口设置一个新的标识
- GWL_USERDATA:设置与窗口相关的32位值
- 如果hWnd参数指定的是一个对话框,还可以取值:
- DWL_DLGPROC:设置新的对话框过程
- DWL_MSGRESULT:设置在对话框过程中处理的消息返回值
- DWL_USER:设置新的额外信息,该信息仅为应用程序所有,例如句柄或指针
- dwNewLong:指定设置的新值
- 修改时机,即在哪里调用SetWindowLong函数?
- WM_CREATE消息的响应函数-不合适:在响应WM_CREATE消息时,对话框的子控件还未创建完成,只有在此消息处理完毕之后,对话框及其子控件才全部创建完成。
- WM_INITDIALOG消息:当对话框及其上的子控件创建完成,将要显示之前,会发送此消息——在VS2005中,此消息被OnInitDialog函数替代。
- 所以:将SetWindowLong函数写到OnInitDialog函数中
- 获得窗口句柄的三种方式
- 1 GetNextWindow函数
- SDK函数用法:GetNextWindow(hwnd, GW_HWNDNEXT);
- CWnd的此函数:GetFocus()->GetNextWindow();
- 2 GetWindow函数
- 功能:该函数返回与指定窗口有特定关系的窗口句柄
- SDK原型:HWND GetWindow(HWND hWnd,UINT uCmd);
- 第一个参数是开始查找的窗口的句柄
- 第二个参数指定hWnd参数指定的窗口与要获得的窗口之间的关系,取值
- GW_HWNDNEXT,则查找在Z次序中位于指定窗口下面的窗口
- GW_HWNDPREV,则查找在Z次序中位于指定窗口前面的窗口
- CWnd的此函数:GetWindow(GW_HWNDNEXT);
- 3 GetNextDlgTabItem函数
- 该函数返回指定控件前面或后面的一个具有WS_TABSTOP风格的控件。
- WS_TABSTOP风格:属性下选中“Tab stop”选项
- 原型:HWND GetNextDlgTabItem(HWND hDlg, HWND hCtl, BOOL bPrevious);
- 参数含义:
- hDlg:指定将被搜索的对话框
- HCtl:指定用来作为搜索开始点的控件
- hPrevious:指定搜索的方向——TRUE:上一个;FALSE:下一个。
- 另一种简单的实现方式
- 在IDOK的响应函数中添加代码:
- GetNextDlgTabItem(GetFocus())->SetFocus();