1. 首先插入一个对话框资源,用Insert→Resource→Dialog。对于MFC自动提供的帮助→关于选项打开后的一个对话框是可以改变的。在新建的Dialog的构造函数中,调用了基类的CDialog。: CDialog(CTestDlg::IDD, pParent),IDD在头文件中定义为:enum { IDD = IDD_DIALOG1 };,即我们添加的对话框的ID。
CDialog类可以生成模态对话框和非模态对话框MSDN如下说:
The CDialog class is the base class used for displaying dialog boxes on the screen. Dialog boxes are of two types: modal and modeless. A modal dialog box must be closed by the user before the application continues. A modeless dialog box allows the user to display the dialog box and return to another task without canceling or removing the dialog box
2. 创建对话框窗口
①创建模态对话框 CTestDlg dlg; dlg.DoModal();其中模态对话框是由CDialog::EndDialog关闭的。
②非模态对话框:以View类作为父窗口(框架类作为父窗口效果一样),然后要调用显示的函数ShowWindow。由于CTestDlg dlg为局部变量,故点击时不会出现对话框窗口,它随着局部变量的销毁而销毁了。有2种方法可以解决,一种是将CTestDlg dlg设为成员变量,还有一种是利用指针在堆上分配内存,
CTestDlg *pDlg=new CTestDlg(); pDlg->Create(IDD_DIALOG1,GetParent());
pDlg->ShowWindow(SW_SHOW);这种方法需要在析构函数中将其Delete掉。
当点击OK按钮时,模态对话框是将对话框销毁掉,而非模态则只是将其隐藏掉。
3. 添加一个按钮,当该按钮被按下时,动态添加一个按钮。
m_btn.Create((LPCTSTR)"火箭",BS_DEFPUSHBUTTON | WS_CHILD |WS_VISIBLE,CRect(0,0,40,40),this,100);需添加上WS_VISIBLE,或者用ShowWindow来显示也可以。第一个参数在VS2005中一定要我转换为LPCTSTR,在vc6.0中貌似不用的吧。
可以用一个成员变量来判断创建的按钮是否被单击过了,或者定义一个static变量也行,最方便的是判断CTestDlg函数的句柄就行,如果句柄不存在,则添加,否则,删除。
if(!m_btn.m_hWnd)
{
m_btn.Create((LPCTSTR)"火箭",BS_DEFPUSHBUTTON | WS_CHILD |WS_VISIBLE,CRect(0,0,40,40),this,100);
}
else
{
m_btn.DestroyWindow();
}
4. 添加静态文本框控件和编辑框
按住Ctrl+左键拖动就可以达到复制功能,编辑框也一样。下面,我们要做的是点击静态文本框后使得它的值变为“数值1:”,if(GetDlgItem(IDC_NUMBER1)->GetWindowText(str),str=="Number1:")
{
GetDlgItem(IDC_NUMBER1)->SetWindowText(_T("数值:"));
}
else
{
GetDlgItem(IDC_NUMBER1)->SetWindowText(_T("Number1:"));
}
这里用_T的原因是_T("")是一个宏,他的作用是让你的程序支持Unicode编码因为Windows使用两种字符集ANSI和UNICODE,前者就是通常使用的单字节方式,但这种方式处理象中文这样的双字节字符不方便,容易出现半个汉字的情况。而后者是双字节方式,方便处理双字节字符。如果你编译一个程序为ANSI方式,_T实际不起任何作用。而如果编译一个程序为UNICODE方式,则编译器会把"Hello"字符串以UNICODE方式保存。_T和_L的区别在于,_L不管你是以什么方式编译,一律以UNICODE方式保存。
而vs2005默认是Unicode编码,这就是为什么上面会出现乱码的原因。修改方法只要把(LPCTSTR)改为_T就可以了。
5. 输入数字,然后将加的结果放到edit3中。
有7中方法:
n GetDlgItem()->Get(Set)WindowText()
n GetDlgItemText()/SetDlgItemText()
n GetDlgItemInt()/SetDlgItemInt()
n 将控件和整型变量相关联
n 将控件和控件变量相关联
n SendMessage()
n SendDlgItemMessage()
这里有几点要注意的:
int num1,num2,num3;
WCHAR ch1[10],ch2[10],ch3[10];
字符数组需要定义为WCHAR型,原因和上面说的一样因为是在vs2005中编译的,然后宽字符转换为int型则需要用函数:num1=_wtoi(ch1); num2=_wtoi(ch2); int转宽字符则是_itow(num3,ch3,10);
将控件和整型变量相关联中,DoDataExchange调用CWnd::UpdateData()。作用是Call this member function to initialize data in a dialog box, or to retrieve and validate dialog data。当参数为true时,获取编辑框中的数据,而为false时则是初始化编辑框中数据。
代码如下:UpdateData(); m_num3=m_num1+m_number2; UpdateData(false);
利用消息的方法是用到SendMessage中的参数WM_GETTEXT,WM_SETTEXT,EM_SETSEL,其中EM_SETSEL是设置复选框用的,要显示的是有复选的内容,则需要将焦点放到edit3上来。
6. 单击收缩<<按钮实现对话框的剪切代码如下
CString str;
// TODO: 在此添加控件通知处理程序代码
if(GetDlgItemText(IDC_BUTTON1,str),str=="收缩<<")
{
SetDlgItemText(IDC_BUTTON1,_T("扩展>>"));
}
else
{
SetDlgItemText(IDC_BUTTON1,_T("收缩<<"));
}
static CRect rectLarge;
static CRect rectSmall;
CRect rectSeparator;
if(rectLarge.IsRectEmpty())//这里为什么要判断呢,因为每一次单击按钮得到的矩形区域是不一样的不判断的话只能执行一次后就不能再扩展了。
{
GetDlgItem(IDC_SEPARATOR)->GetWindowRect(&rectSeparator);
GetWindowRect(&rectLarge);
rectSmall.left=rectLarge.left;
rectSmall.top=rectLarge.top;
rectSmall.right=rectLarge.right;
rectSmall.bottom=rectSeparator.bottom;
}
if(str=="收缩<<")
{
SetWindowPos(NULL,0,0,rectSmall.Width(),rectSmall.Height(),SWP_NOMOVE | SWP_NOZORDER);
}
else
{
SetWindowPos(NULL,0,0,rectSmall.Width(),rectLarge.Height(),SWP_NOMOVE |SWP_NOZORDER);
}
刚刚SetWindowPos这个函数的时候出了情况,调试了好久,终于发现了,原来是最后面的括号打错了.囧
8.Z-order
窗口的Z次序表明了重叠窗口堆中窗口的位置,这个窗口堆是按一个假想的轴定位的,这个轴就是从屏幕向外伸展的Z轴。Z次序最上面的窗口覆盖所有其它的窗口,Z次序最底层的窗口被所有其它的窗口覆盖。应用程序设置窗口在Z次序中的位置是通过把它放在一个给定窗口的后面,或是放在窗口堆的顶部或底部。
Windows系统管理三个独立的Z次序——一个用于顶层窗口、一个用于兄弟窗口,还有一个是用于最顶层窗口。最顶层窗口覆盖所有其它非最顶层窗口,而不管它是不是活动窗口或是前台窗口。应用程序通过设置WS_EX_TOPMOST风格创建最顶层窗口。
一般情况下,Windows系统把刚刚创建的窗口放在Z次序的顶部,用户可通过激活另外一个窗口来改变Z次序;Windows系统总是把活动的窗口放在Z次序的顶部,应用程序可用函数BringWindowToTop把一个窗口放置到Z次序的顶部。函数SetWindowPos和DeferWindowPos用来重排Z次序。
窗口:
n 兄弟窗口
共享同一个父窗口的多个子窗口叫兄弟窗口。
n 活动窗口
活动窗口是应用程序的顶层窗口,也就是当前使用的窗口。只有一个顶层窗口可以是活动窗口,如果用户使用的是一个子窗口,Windows系统就激活与这个子窗口相应的顶层窗口。
任何时候系统中只能有一个顶层窗口是活动的。用户通过单击窗口(或其中的一个子窗口)、使用ALT+TAB或ALT+ESC组合键来激活一个顶层窗口,应用程序则调用函数SetActiveWindow来激活一个顶层窗口。
n 前台窗口和后台窗口
在Windows系统中,每一个进程可运行多个线程,每个线程都能创建窗口。创建正在使用窗口的线程称之为前台线程,这个窗口就称之为前台窗口。所有其它的线程都是后台线程,由后台线程所创建的窗口叫后台窗口。
用户通过单击一个窗口、使用ALT+TAB或ALT+ESC组合键来设置前台窗口,应用程序则用函数SetForegroundWindow设置前台窗口。如果新的前台窗口是一个顶层窗口,那么Windows系统就激活它,换句话说,Windows系统激活相应的顶层窗口。
9.按下enter键使得鼠标指针在对话框中的位置有edit1移到edit2中。
利用函数SetWindowLong通过调用自己写的响应函数覆盖基类的,不过这种方法只能响应一次,第二次就传递不下去了。
要使按下enter键有响应,应该先覆盖基类的IDOK响应函数。将其响应的//OnOK();注释起来(双击确定按钮)。
WNDPROC prewProc;
LRESULT CALLBACK WinRocProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
if(uMsg==WM_CHAR && wParam==13)
{
::SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));
//::SetFocus(::GetNextWindow(hwnd,GW_HWNDNEXT));
//SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,FALSE));
//HWND GetNextDlgTabItem(
//HWND hCtl, // handle to known control
//BOOL bPrevious // direction flag
//);
return 1;
}
else
{
return prewProc(hwnd,uMsg,wParam,lParam);
}
}
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// TODO: 在此添加额外的初始化
prewProc=(WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,(LONG)WinRocProc);
return TRUE; // return TRUE unless you set the focus to a control
//这里要注意类型的转换
}
10.在利用缺省按钮的响应函数OnOK中实现上面的过程
当我们在窗口中按Enter键时,会由缺省按钮的响应函数执行,在其父类中以定义,我们在其子类中覆盖。
CWnd::GetNextDlgTabItem
CWnd* GetNextDlgTabItem( CWnd* pWndCtl, BOOL bPrevious = FALSE ) const;
按顺序查找具有TABSTOP属性的控件。LAYOUT中Tab Order显示TABSTOP的顺序
GetNextDlgTabItem(GetFocus())->SetFocus();
注意:按下Enter键时,将会调用styles中设置为缺省按钮的响应函数。
即使不存在OK键,仍然由ONOK响应ENTER
自己增加OK按钮时,要注意将OK按钮的ID号设置为IDOK,而不是IDC_OK就能响应缺省响应函数.
另外两种利用GetNextWindow和GetWindow再设焦点的方法均会报错。