1.对于OnInitDialog是对WM_INITDIALOG消息的自动响应进行调用的,重载时在其中应该如下写:
声明:
virtual BOOL OnInitDialog();//This method is called in response to the WM_INITDIALOG message.
定义:
BOOL ConnectConfigServerTab::OnInitDialog()
{
CDialog::OnInitDialog();/* 基类的OnInitDialog会调用一次UpdateData(FALSE) */
InitControls();
UpdateData(FALSE);/* 如果InitControls中对关联变量有更改,则会更新到界面上 */
return TRUE;
}
2.对话框中常用到“确定”和“取消”按钮,可以将对话框中任意的按钮ID设置为IDOK来响应OnOK()消息响应,设置为IDCANCEL来调用OnCancel()消息响应。要注意的是OnCancel()消息调用在用“X”关闭对话框时同样调用OnCancel()消息响应。
3.对话框是MFC中常用的窗口之一,而MFC对话框的默认的样式在很多应用中都显得很单调,如何对对话框的样式和风格进行修改是很多开发者需要面临的问题,本文从MFC的CDialog派生出自己的对话框样式,给出了很多改变对话框样式的函数,通过调用这些函数,就可以很好的改变对话框的各种风格。
主要的函数有:
//设置背景刷
void SetDlgBKBrush( CBrush* pBrush );
//设置背景颜色
void SetDlgBKColor( COLORREF clrBk );
//设置对话框字体
void SetFont( CFont* pFont );
//这只标题栏文字的位置,CAPTIONTEXT_LEFT,CAPTIONTEXT_CENTER,CAPTIONTEXT_RIGHT
void SetCaptinTextPosition(int positionType);
//设置标题文字的颜色,激活状态,非激活状态
void SetCaptionTextColor(COLORREF clrActiveCaptionText,COLORREF clrInactiveCaptionText);
//设置标题栏颜色,激活和非激活状态
void SetCaptionBarColor(COLORREF clrActiveCaptionBar,COLORREF clrInactiveCaptionBar);
//设置边框的位图,参数为各个边框图的路径
BOOL SetBorderBmp(LPCTSTR lpbmpLeft,LPCTSTR lpbmpRight,LPCTSTR lpbmpBottom)
//设置边框的位图,参数为各个边框图的句柄
BOOL SetBorderBmp(HBITMAP hbmpLeft,HBITMAP hbmpRight,HBITMAP hbmpBottom)
//设置标题栏上各个按钮的位图,给出路径
BOOL SetCaptionBtnBmp(CStringArray& strArrBtnClose,CStringArray& strArrBtnMin,CStringArray& strArrBtnMax)
//设置标题栏上各个按钮的位图,给出位图句柄指针
BOOL SetCaptionBtnBmp(HBITMAP* hbmpBtnClose,HBITMAP* hbmpBtnMin,HBITMAP* hbmpBtnMax, int nElement)
//设置标题栏的位图,给出位图句柄
BOOL SetCaptionBmp(HBITMAP hbmpACaption,HBITMAP hbmpNCaption)
//设置标题栏的位图,给出位图路径
BOOL SetCaptionBmp(LPCTSTR lpszACaption,LPCTSTR lpszNCaption)
//设置对话框的各边界的颜色
void SetBorderColor(COLORREF clrLeft,COLORREF clrTop,COLORREF clrRight, COLORREF clrBottom);
从上面可以看出,主要包括有:修改对话框的背景颜色,标题栏的颜色,标题栏的位图,标题栏字体的位置和颜色,包括激活和非激活状态,对话框边界的颜色,对话框字体等。
4.注意使用模态对话框(DoModal()来显示对话框)时如果对话框是new出来的,则在DoModal结束后应该释放掉对话框对象,因为如果对象再次调用DoModal会造成内存泄露。
同样对于非模态对话框,不能重复调用Create来创建对话框,同样会造成内存泄露。
5.对于模态对话框,不会产生BUG的用法是用局部变量的对话框对象,这样在DoModal结束后对象就析构了,在下次再次显示对话框时使用新的局部变量,这样不会造成对话框初始化中的种种问题。但有些情况下我们希望保留对话框的指针,通过一些其他函数中的操作修改对话框内容的话,就可能用动态创建(new())的方法创建一个用类的成员变量保存的对话框对象,但这时应该注意,在多次调用对话框的过程中,最好保证这个对话框对象都是新创建的,因为我们在上次调用DoModal()后对话框的成员变量在OnInitDialog()中被初始化了,而再次调用OnInitDialog()中使用CDialog::OnInitDialog()就会出错。
6.对于5中提到的情形,还有一点要注意,就是在对话框被DoModal显示而没有返回前,不应该delete或者说释放对话框对象的内存,否则程序跑飞。
7.今天遇到一个让对话框上"X"关闭按钮无效的需求,想到如下几种方法,记录下:
(1)刚开始是希望通过在OnInitDialog中根据需求修改对话框属性的方法:
if(不显示"X")
{
this->ModifyStyle(WS_SYSMENU, /* 移除系统菜单属性 */
0);
}
这种方式虽能实现功能,但"X"按钮直接消失了,并且会使对话框的图标、最大化和最小化按钮也消失了。
(2)希望通过重载CWnd::PreCreateWindow()函数在创建前就修改对话框属性:
事实证明这种想法是错误的,原因如下:
一般的窗口的创建是使用Create函数,这个函数在创建窗口之前调用了PreCreateWindow函数,并且允许在创建创建之前在PreCreateWindow注册一个拥有自定义窗口样式的新的窗口类,来创建一个拥有自定义类名新的窗口。而模式对话框是通过CreateDialogIndirect来创建的,在这当中并没有调用PreCreateWindow函数,重载的PreCreateWindow函数根本就不被执行,因此在这个函数里修改对话框的窗口类是没有用的。
CDialog是通过CDialog::DoModal()函数创建窗口的,下面是MFC中DoModal函数的代码:
int CDialog::DoModal()
{
// 载入资源
LPCDLGTEMPLATE lpDialogTemplate = m_lpDialogTemplate;
HGLOBAL hDialogTemplate = m_hDialogTemplate;
HINSTANCE hInst = AfxGetResourceHandle();
if (m_lpszTemplateName != NULL)
{
hInst = AfxFindResourceHandle(m_lpszTemplateName, RT_DIALOG);
HRSRC hResource = ::FindResource(hInst, m_lpszTemplateName, RT_DIALOG);
hDialogTemplate = LoadResource(hInst, hResource);
}
if (hDialogTemplate != NULL)
lpDialogTemplate = (LPCDLGTEMPLATE)LockResource(hDialogTemplate);
if (lpDialogTemplate == NULL)
return -1;
HWND hWndParent = PreModal();
AfxUnhookWindowCreate();
BOOL bEnableParent = FALSE;
if (hWndParent != NULL && ::IsWindowEnabled(hWndParent))
{
::EnableWindow(hWndParent, FALSE);
bEnableParent = TRUE;
}
TRY
{
// 创建无模式对话框
AfxHookWindowCreate(this);
if (CreateDlgIndirect(lpDialogTemplate,
CWnd::FromHandle(hWndParent), hInst))
{
if (m_nFlags & WF_CONTINUEMODAL)
{
// 进入模式循环
DWORD dwFlags = MLF_SHOWONIDLE;
if (GetStyle() & DS_NOIDLEMSG)
dwFlags |= MLF_NOIDLEMSG;
VERIFY(RunModalLoop(dwFlags) == m_nModalResult);
}
// hide the window before enabling the parent, etc.
if (m_hWnd != NULL)
SetWindowPos(NULL, 0, 0, 0, 0, SWP_HIDEWINDOW|
SWP_NOSIZE|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
}
}
CATCH_ALL(e)
{
......
}
END_CATCH_ALL
if (bEnableParent)
::EnableWindow(hWndParent, TRUE);
if (hWndParent != NULL && ::GetActiveWindow() == m_hWnd)
::SetActiveWindow(hWndParent);
// destroy modal window
DestroyWindow();
PostModal();
// 解锁、释放资源
if (m_lpszTemplateName != NULL || m_hDialogTemplate != NULL)
UnlockResource(hDialogTemplate);
if (m_lpszTemplateName != NULL)
FreeResource(hDialogTemplate);
return m_nModalResult;
}
在这个函里先是载入了对话框资源,然后通过LockResource函数,使DLGTEMPLATE类型指针指向相关的内存,然后把这个指针作为参数传递给了CreateDlgIndirect函数(调用了::CreateDialogIndirect)。DLGTEMPLATE的定义如下:
typedef struct {
DWORD style;
DWORD dwExtendedStyle;
WORD cdit;
short x;
short y;
short cx;
short cy;
} DLGTEMPLATE, *LPDLGTEMPLATE;
这个结构体保存着创建对话框需要的样式、位置等信息,在DoModal函数里它是通过对话框资源得到的,那对话框资源里一定有它需要的东西。下面是从rc文件中摘录的对话框的信息:
IDD_AAAA_DIALOG DIALOGEX 0, 0, 320, 200
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_APPWINDOW
CAPTION "aaaa"
FONT 9, "宋体"
BEGIN
DEFPUSHBUTTON "确定",IDOK,260,10,50,14
PUSHBUTTON "取消",IDCANCEL,260,23,50,14
LTEXT "TODO: 在这里设置对话控制。",IDC_STATIC,50,90,200,8
END
第一行是对话框的位置信息,第二行是对话框的样式,第三行是扩展样式,它们的内容就是在对话框编辑器修改属性时得到的内容。
重载PreCreateWindow的目的不外乎是想在其中修改默认的窗口类的样式信息,然后达到修改窗口样式的目的。而对话框能在资源编辑器里修改它的所有应有的样式,而这些样式在DoModal函数里能被读出来,并传递给CreateDialoagIndirect函数,创建对话框。
由于不能在PreCreateWindow函数里注册新的窗口类,所有的MFC程序的对话框的类名都是相同的:#32770。
(3)第三种方法,可以使"X"按钮灰显:
CMenu* pSysMenu = GetSystemMenu(FALSE); /* 取系统菜单 */
int i = pSysMenu->GetMenuItemID(1) ; /* 得到"X"号所在菜单项 */
pSysMenu->EnableMenuItem(i,MF_DISABLED ); /* 使灰显 */
8.对于对话框上的图标显示:
对话框上的图标需要自己添加,方法如下:
HICON m_hIcon = AfxGetApp()->LoacIcon(IDR_MAINFRAM);
this->SetIcon(m_hIcon,TRUE); /*设置大图标*/
this->SetIcon(m_hIcon,FALSE); /* 设置小图标 */
需要注意的是:(1)不设定图标则对话框不会显示图标,不会从父窗口继承;
(2)对话框边框使用Resizing时强制要求对话框具有图标,如果未设置则有一个空白图标。