对话框用户界面程序的编写,如何向对话框控件联接数据成员及其实现机理,如何向对话框控关联控件类,如何利用对话框类的成员函数向控件发送消息和获取对话框控件的类指针,如何直接利用对话框控件类操纵对话框控件(发送消息和直接调用成员函数)。如何在程序运行时产生和销毁控件。对话框控件的几种操作方式的优劣比较分析。如何实现对话框的部分收缩和展开。如何让对话框上的文本框在程序启动后立即获得焦点,如何利用SetWindowLong改变窗口的回调函数,通过改变文本框的默认回车处理方式进行演示。实现多个输入文本框间通过回车逐一向下传递焦点的另一种巧妙方法(用缺省按钮来处理)。 1.添加一个对话框,可用插入-》资源-》Dialog即可;也可用资源工具栏选择新建对话。 在MFC中对资源的操作一般是通过一个类来操作的,对对话框的操作是用CDialog来操作的。对话框有两种类型,模态(modal,应用程序继续之前此对话框必须被关闭)和非模态的(modeless) 2.创建一个类跟资源相关联:可以双击对话框资源,打开类向导来创建一个新的类,可取名为CTestDlg,然后MFC会创建TestDlg的头文件和源文件。(可用change改名) 创建一个菜单项,如IDM_DIALOG 对话框 ,然后打开类向导,选择COMMAND消息,增加函数(这里放到view类)。 然后用virtual int DoModal( );//创建一个模态对话框 void EndDialog( int nResult ); //关闭一个模态对话框 OnDialog函数中: CTestDlg dlg; //注意view类不认识CTestDlg,所以要加头文件#include "TestDlg.h" dlg.DoModal(); 用CDilag::Create可以创建一个非模态的对话框 BOOL Create( LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL ); //附加到一个CDialog对象 BOOL Create( UINT nIDTemplate, CWnd* pParentWnd = NULL ); 可通过对话框的名字或ID号创建,若后面的CWnd设置为NULL,这个对话框对象的父窗口将被设置为主程序窗口。 创建了对话框后,还有用ShowWindow将它显示出来(CWnd的成员函数,CDialog也有) 但创建模态对话框的时候,显示出模态对话框的时候程序已经暂停,所以CDialog的生命周期还没有结束;而创建非模态的时候,程序没有暂停,所以它的生命周期就结束了。 解决办法:1.把CDialog设为成员变量;2.创建一个指针,开辟新的内存 void CMyboleView::OnDialog() { CTestDlg *pDlg=new CTestDlg(); pDlg->Create(IDD_DIALOG1,this); pDlg->ShowWindow(SW_SHOW); } //但这样每次点击一次“对话框”菜单就会产生一个对话框……就会有多个对话框了 而pDlg是局部变量,当其生命周期结束后,所指向的内存就无法管理了,所以要定义为成员变量,然后在析构函数中将内存删除。 而对话框的OK按钮响应的是CDialog的OnOK函数。若是在非模态上,就必须覆盖此成员函数,然后调用DestroyWindow。不要调用基类的成员函数,因为它调用EndDialog,只是将对话框隐藏而不是destroy。 3.动态创建按钮 在资源 对话框上,用控件工具箱添加一个Add按钮,ID为IDC_BTN_ADD。然后对它建立类向导,可选择它的通告消息BN_CLICKED或BN_DOUBLECLICKED,这里在CTestDlg中添加一个单击函数OnBtinAdd()。 用CButton的BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID ); //创建按钮,dwStyle就是BS_CHECKBOX或WS_CHILD那些 (貌似) m_btn.Create("维新",BS_DEFPUSHBUTTON | WS_VISIBLE | WS_CHILD, CRect(0,0,100,100),this,123); 但这样双击一次后产生了一个维新按钮,但按第二次的时候程序就有异常了。 这里新建一个BOOL型的成员变量m_bIsCreate(private的),初始化为FALSE。 然后判断 if(!m_bIsCreate)……再把它变为TRUE即可。 也可在else中销毁窗口,用DestroyWindow,再把m_bIsCreate设置为FALSE 也可定义一个静态的局部变量来保存bIsCreate,如static BOOL bIsCreate=FALSE; //第一次已经分配了内存空间了,以后就不会再分配了,所以只初始化一次 也可判断这个对象的m_hWnd是否为空(就表明是否已经建立按钮,与某个父窗口相关) 4.静态的文本框控件Static:添加多个控件,可按住Ctrl键,点击已经建立的控件,然后拖动位置即可。 然后添加一些编辑框Edit。可用Ctrl选中多个控件,然后再点击左下角的对齐按钮,也可点击跳转为相同大小,也可调整控件之间的间隔。 静态文本框主要是标记作用的,所以他们的ID号都是一样的。要接收消息的话,就要更改ID号,否则在classwizard中看不到它的ID号。 对着NUMBER1静态文本控件点类向导,选择BN_CLICKED通告消息。可用GetWindowText获取一个窗口的文本。 可用CWnd* GetDlgItem( int nID ) const; void CWnd::GetDlgItem( int nID, HWND* phWnd ) const; //获取一个对话框的对象指针,用ID号识别 用SetWindowText设置窗口的文本设置。 但对于默认的静态文本框是不接收通告消息的,要在此控件的属性中选择style,把通知(notify)复选上。 CString str; if(GetDlgItem(IDC_NUMBER1)->GetWindowText(str),str=="Number1") //逗号表达式=最后一个表达式的值 { GetDlgItem(IDC_NUMBER1)->SetWindowText("数字1"); } else { GetDlgItem(IDC_NUMBER1)->SetWindowText("Number1"); } 在两个编辑框中输入数字,然后点击add在第3个编辑框中输出加得的结果。 先用GetWindowText获得对话框的内容,然后用C语言中的atoi将字符串转化为数值。(string to int) && 也有itoa将数值转化为字符串char *_itoa( int value, char *string, int radix ); //value是将转换的数值,string是存放转换后的字符串,radix表示进制(2-36) int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10); //获取控件内容,10表示buffer大小 GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10); num1=atoi(ch1); num2=atoi(ch2); //字符串转数值 num3=num1+num2; itoa(num3,ch3,10); //数值转字符串,10表示进制 GetDlgItem(IDC_EDIT3)->SetWindowText(ch3); //设置控件内容 另外一个函数,CWnd::GetDlgItemText获取对话框文本内容 int GetDlgItemText( int nID, LPTSTR lpStr, int nMaxCount ) const; int GetDlgItemText( int nID, CString& rString ) const; //同理也可用SetDlgItemText来设置对话框文本 例子: GetDlgItemText(IDC_EDIT1,ch1,10); GetDlgItemText(IDC_EDIT2,ch2,10); itoa(atoi(ch1)+atoi(ch2),ch3,10); SetDlgItemText(IDC_EDIT3,ch3); 另外一个:CWnd的GetDlgItemInt获取控件内容转换为数值 UINT GetDlgItemInt( int nID, BOOL* lpTrans = NULL, BOOL bSigned = TRUE ) const; //若出现错误(若控件内容为非文本或超出范围),则lpTrans为0;bSigned说明转换的值是否有符号 同理也有void SetDlgItemInt( int nID, UINT nValue, BOOL bSigned = TRUE ); 例子: num1=GetDlgItemInt(IDC_EDIT1);//前面不加(int)转换类型也可以??不是返回UINT吗 num2=GetDlgItemInt(IDC_EDIT2); //自动转换吗 num3=num1+num2; SetDlgItemInt(IDC_EDIT3,num3); 可在类向导中,选择成员变量,选中IDC_EDIT然后add variable增加变量,如m_num1,种类为Value,类型为int。 这样MFC就在头文件里声明了,然后在构造函数里初始化为0了。这样控件的初始内容就是0了。 在成员函数DoDataChange中,将m_num1与控件文本相关联……这里是DDX_TEXT来关联变量到控件去。有很多DDX开头的函数。 DoDataChange是被框架调用来交换和调用对话框数据。但我们从来不直接调用这个函数,一般是通过UpdateData来调用。UpdateDate是初始化一个对话框或从对话框获取数据。 BOOL UpdateData( BOOL bSaveAndValidate = TRUE ); //若为FALSE则表示正在初始化;TRUE表示正在接收数据 当一个模态对话框创建后,框架自动调用UpdateData,并把bSaveAndValidate设置为FALSE进行初始化。 UpdateData(); //获取内容 m_num3=m_num2+m_num1; UpdateData(FALSE); //初始化到控件 可在类向导中为刚才绑定到控件的成员变量设置范围,如m_num1可设置最大值、最小值。这时MFC在DoDateExchange中又添加了DDV_MinMaxInt函数。 在类向导中,为EDIT1再添加一个成员变量,m_edit1,control类型,CEdit的。 例子: int num1,num2,num3; char ch1[10],ch2[10],ch3[10]; m_edit1.GetWindowText(ch1,10); m_edit2.GetWindowText(ch2,10); num1=atoi(ch1); num2=atoi(ch2); num3=num1+num2; itoa(num3,ch3,10); m_edit3.SetWindowText(ch3); 获取文本的消息是WM_GETTEXT WM_GETTEXT wParam = (WPARAM) cchTextMax; // number of characters to copy lParam = (LPARAM) lpszText; // address of buffer for text 可以用SendMessage发送一个消息获得文本, LRESULT SendMessage( HWND hWnd, // handle of destination window UINT Msg, // message to send WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); 例子: // ::SendMessage(GetDlgItem(IDC_EDIT1)->m_hWnd,WM_GETTEXT,10,(LPARAM)ch1); // ::SendMessage(m_edit1.m_hWnd,WM_GETTEX,10,(LPARAM)ch1); GetDlgItem(IDC_EDIT1)->SendMessage(WM_GETTEXT,10,(LPARAM)ch1); m_edit1.SendMessage(WM_GETTEXT,10,(LPARAM)ch1); 然后再发送WM_SETTEXT消息,设置文本。 WM_SETTEXT wParam = 0; // not used; must be zero lParam = (LPARAM)(LPCTSTR)lpsz; // address of window-text string 例子: m_edit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3); 另外:CWnd的SendDlgItemMessage发送对话框消息。 LRESULT SendDlgItemMessage( int nID, UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ); //相当于先用GetDlgItem然后再用SendMessage 如: SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,0,(LPARAM)ch3); 另外:对EDIT控件有:EM_GETSEL消息,(这是设置复选部分) wParam = (WPARAM) (LPDWORD) lpdwStart; // receives starting position lParam = (LPARAM) (LPDWORD) lpdwEnd; // receives ending position EM_SETSEL //选择一部分字符 wParam = (WPARAM) (INT) nStart; // starting position lParam = (LPARAM) (INT) nEnd; // ending position 但要看到复选部分的话,要用SetFocus设置焦点。若开始位置设置为0,结束位置设置为-1,则整个控件的文本将被选中。 SendDlgItemMessage(IDC_EDIT3,EM_SETSEL,0,-1); //选中控件全部内容 5.访问对话框控件的七种方式总结: GetDlgItem()->Get(Set)WindowText() GetDlgItemText()/SetDlgItemText() GetDlgItemInt()/SetDlgItemInt() 将控件和整型变量相关联 将控件和控件变量相关联 SendMessage() SendDlgItemMessage() //最常用的是第一种,和将控件与变量关联。用得最少的就是SendMessage。 6.添加一个按钮“收缩<<”,双击它就提示增加一个成员函数(BN_CLICKED消息) 用图像控件,拉成一条线,ID号改为IDC_SEPARATOR,分隔符。选择style上的sunken(凹陷),设置好后,可点击左下角的图标,测试运行对话框看效果。 定义两个静态的CRect局部变量rectLarge和rectSmall。CRect可用IsRectEmpty检测矩形是否为空,当宽度和/或高度是0的时候矩形就是空的。IsRectNull检测矩形top、bottom、left、right是否都是0。 用GetWindowRect获取窗口的矩形void GetWindowRect( LPRECT lpRect ) const; 用BOOL SetWindowPos( const CWnd* pWndInsertAfter, int x, int y, int cx, int cy, UINT nFlags ); //设置窗口的大小、形状、z次序,pWndInsertAfter(在此CWnd的Z次序之后)可设置为一个CWnd或者是wndBottom/wndTop/wndTopMost/wndNoTopMost,x/y是左上角的左边,cx和cy指的是宽度和长度。uFlags可设置窗口一些属性,如SWP_NOACTIVATE,SWP_NOMOVE(忽略x、y),SWP_NOZORDER(忽略pWndInsertAfter),SWP_NOSIZE(忽略cx、cy) 可用CRect的width和Height函数获取宽度和高度,可设置分隔线的visible去掉将它隐藏掉。可在style中设置按钮为默认按钮default button。 Z-order 窗口的Z次序表明了重叠窗口堆中窗口的位置,这个窗口堆是按一个假想的轴定位的,这个轴就是从屏幕向外伸展的Z轴。Z次序最上面的窗口覆盖所有其它的窗口,Z次序最底层的窗口被所有其它的窗口覆盖。应用程序设置窗口在Z次序中的位置是通过把它放在一个给定窗口的后面,或是放在窗口堆的顶部或底部。&& Windows系统管理三个独立的Z次序——一个用于顶层窗口、一个用于兄弟窗口,还有一个是用于最顶层窗口。最顶层窗口覆盖所有其它非最顶层窗口,而不管它是不是活动窗口或是前台窗口。应用程序通过设置WS_EX_TOPMOST风格创建最顶层窗口。 一般情况下,Windows系统把刚刚创建的窗口放在Z次序的顶部,用户可通过激活另外一个窗口来改变Z次序;Windows系统总是把活动的窗口放在Z次序的顶部,应用程序可用函数BringWindowToTop把一个窗口放置到Z次序的顶部。函数SetWindowPos和DeferWindowPos用来重排Z次序。 窗口 兄弟窗口 共享同一个父窗口的多个子窗口叫兄弟窗口。 活动窗口 活动窗口是应用程序的顶层窗口,也就是当前使用的窗口。只有一个顶层窗口可以是活动窗口,如果用户使用的是一个子窗口,Windows系统就激活与这个子窗口相应的顶层窗口。 任何时候系统中只能有一个顶层窗口是活动的。用户通过单击窗口(或其中的一个子窗口)、使用ALT+TAB或ALT+ESC组合键来激活一个顶层窗口,应用程序则调用函数SetActiveWindow来激活一个顶层窗口。 前台窗口和后台窗口 在Windows系统中,每一个进程可运行多个线程,每个线程都能创建窗口。创建正在使用窗口的线程称之为前台线程,这个窗口就称之为前台窗口。所有其它的线程都是后台线程,由后台线程所创建的窗口叫后台窗口。 用户通过单击一个窗口、使用ALT+TAB或ALT+ESC组合键来设置前台窗口,应用程序则用函数SetForegroundWindow设置前台窗口。如果新的前台窗口是一个顶层窗口,那么Windows系统就激活它,换句话说,Windows系统激活相应的顶层窗口。 7. SetWindowLong可改变一个窗口的属性 LONG SetWindowLong( HWND hWnd, // handle of window int nIndex, // offset of value to set LONG dwNewLong // new value ); //nIndex是GWL_WNDPROC、GWL_STYLE那些,对于对话框时可用DWL_DLGPROC/DWL_USER //dwNewLong是放置新的值 //若函数成功,就返回先前的窗口的属性值 在CTestDlg右键添加消息处理,WM_INITDIALOG,然后用SetWindowLong改变窗口函数 HWND GetNextWindow( HWND hWnd, // handle to current window UINT wCmd // direction flag,可以是GW_HWNDNEXT,GW_HWNDPREV(相当于方向) ); //获取当前窗口的下一个窗口 WNDPROC prevProc; LRESULT CALLBACK WinSunProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { if(uMsg==WM_CHAR && wParam==0x0d) { ::SetFocus(::GetNextWindow(hwnd,GW_HWNDNEXT)); return 1; } else { return prevProc(hwnd,uMsg,wParam,lParam); } } BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); prevProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC, (LONG)WinSunProc); return TRUE; } 但这样对回车仍然没有反应,因为编辑框默认不支持多行multiline,所以不对回车有反应??所以要复选multiline。这样就可按回车将焦点转移到下一个去了。 另一个获取窗口句柄的函数: HWND GetWindow( HWND hWnd, // handle to original window UINT uCmd // relationship flag,GW_CHILD……功能更加强大 ); 上面也可改成: ::SetFocus(::GetWindow(hwnd,GW_HWNDNEXT)); HWND GetNextDlgTabItem( HWND hDlg, // handle of dialog box,对话框 HWND hCtl, // handle of known control,从哪一个控件开始查找 BOOL bPrevious // direction flag,TRUE从先前开始搜索;FALSE就从下一个开始 ); 编辑框的常规属性里有制表站Tab Stop,按钮也有,但静态文本框没有。 则也可用 ::SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,FALSE)); 可以在默认按钮的OnOK中编辑:GetDlgItem(IDC_EDIT1)->GetNextWindow()->SetFocus(); 但这样不能传递下去,可用GetFocus获得当前焦点的窗口句柄。 如: GetFocus()->GetNextWindow()->SetFocus(); //但到后来,获取的GetNextWindow得到空指针时就会出错 用GetWindow也一样的 GetFocus()->GetWindow(GW_HWNDNEXT)->SetFocus(); 而用GetNextDlgTabItem不会有错,因为它是查找有tab stop属性的控件,然后按照顺序查找。在布局菜单下有个tab顺序,可标出tab顺序,可依次点击它们,就可重新设置了。 GetNextDlgTabItem(GetFocus())->SetFocus();
(孙鑫 七)对话框
最新推荐文章于 2021-01-11 14:48:40 发布