用VC++定制通用对话框
对话框概述
一个通用对话框是由Windows设计和编写的,用于完成某一特定工
作,比如打开一个文件或选择一种颜色等。MFC提供了管理每一通用对
话框类型的类,这些类概括在下表中。表1
例如:处理File菜单上Open和Save As命令时使用CFileDialog通
用对话框,而处理Prin和Print Setup命令时则使用CPrintDialog通用
对话框。
用这些对话框构造用户应用是很方便的,但有时这些通用对话框
的外部特性和行为不能满足用户要求,特别是在对话框上需要增加某
些控件或删除某些控件时尤为突出。本文提供给大家一种方法来定制
通用对话框的外部特性和行为,可用自己的代码与MFC提供的类协同处
理这些对话框,以满足用户的不同要求。一方面由于使用了MFC的通用
对话框类,节省了大量的代码编程工作,提高了开发效率;另一方面,采
用自己的代码协同实现,使对话框的功能可以做到灵活多样。本文以F
ile Open命令为例,来阐述这一方法。
定制File Open通用对话框
CFileDialog类封装Windows公共文件对话框。公共文件对话框以
一种与Windows标准一致的方式,提供了实现File Open和File Save A
s对话框(及其他文件选择对话框)的简单方法。
CFileDialog中有一成员变量m_ofn,它是OPENFILENAME类型的结
构。通常,要使用一个F ileDialog对象,需要首先用CFileDialog的构
造函数创建该对象。在对话框被构造之后,且在用DoModal成员函数显
示对话框之前,应用程序可以通过设置m_ofn结构中的值,来对此对话
框中的控件的值或状态进行初始化。例如:应用程序可将m_ofn的lpsz
Title成员设置为所想要的对话框标题;可将m_ofn的指定该对话框的
创建标志Flags成员设置为其各标志位值的多种组合,以达到一定要求
,其中,设置的Flags中的OFN—ALLOWMULTISELECT标志可允许用户选择
多个文件;设置OFN_HIDEREADONLY标志可隐藏Read Only检查框;设置O
FN_OVERWRITEPROMT标志可做到若所选择的文件已存在,则使Save As
对话框产生一个消息框,使用户确认是否重写该文件;等等。有关此结
构的详细说明,包括它的成员列表,参看Windows SDK文档中OPENILE N
AME的介绍。
从以上所述看,仅改变m_ofn的值只能做到简单的定制,若要对通
用对话框的外观、通用对话框已有控件进行进一步调整(功能改变或
删除),以及给通用对话框增加新的控制,则以上简单定制方法是做不
到的。笔者将为大家提供一种方法,下面以一例子来介绍。
本例假定一应用程序要读入某一位图文件,要求对要读入的位图
文件进行预览,具体实现是在File Open通用对话框的基础上增加预显
功能,即每当选中一个位图文件时,就在对话框中显示此文件的位图。
为节省编程工作量,可对Windows提供的File Open通用对话框进行定
制。本例定制对话框采取的方案是直接使用CFileDialog类,设法获取
标准通用对话框资源并对其进行修改,然后编写File Open菜单命令处
理程序和一个钩子函数(HOOK)来处理特殊要求。本例在VC++4.0环境
中创建一个DibPreView项目来实现。
1.获取并改造通用对话框资源
在使用VC++自动创建的应用程序中,系统菜单File项中的Open项
的处理是调用了系统由CommDll.DLL提供的标准File Open对话框,要
实现对其定制就必须截获Open项的命令,用自己的代码来处理,以替代
系统提供的缺省处理。要做到这点并不困难,只要用ClassWizard在合
适的对象(比如文档类或视窗类等)中增加对ID_FILE-OPEN控制的处理
,即增加OnFileOpen函数,编写此函数就能实现对Open命令的处理。在
此函数中首先要创建一个CFileDialog类的对象,该对象的外观和行为
均同标准的File Open对话框相同,要改变该对象的外形或删除原对话
框中的某些控件或增加某些控件,都必须首先设法获取该对象所对应
的标准资源,然后,对其进行相应的修改。获取资源的方法有两种:
方法一:
在Msdev/Include目录中存有以上几个标准通用对话框的资源,以
FileOpen.Dlg、 FotD lg、FindText.Dlg、Color.Dlg和PrnSetup.Dl
g文件方式存在,这些文件其中的一些常量被定义在本目录中的dlgs.h
中。为了定制该对话框,就要以显式方式引用这些资源,要将它们嵌入
到本应用程序的资源中,然后再修改该对话框的资源。具体步骤如下:
(1)用VC++4.0自动创建项目DibPreView的全部文件;
(2)以Text方式打开本项目中的资源文件DibPreView.rc,在此文
件中的Dialog段首行前加上语句:
#include <FileOpen.dlg>
(3)将Afxres.h文件从Msdev/Mfc/Include目录中复制到本项目目
录中,并打开新复制的Afxres.h文件,将下面的语句添加到文件行首:
#include <dlgs.h>
方法二:
在VC++安装盘中的Msdev\Sample\Mfc\General\Clipart目录中存
有资源文件Commdlg.c ,此文件含有Color、File、Open(或Save As)
、Find、Font、Print、Print Setup、Replce 几个通用对话框的资
源,为了以显式方式引用这些资源,可利用VC++的资源编辑器,将所需
的通用对话框资源复制到本例项目资源文件中。
按以上两方法处理完后,在本项目的资源目录列表中的Dialog项
中,出现一标识符为FIE OPENORD的对话框资源,然后利用资源编辑器
对此标准File Open对话框(即FILEOPENORD标识的)进行改造。主要是
在原对话框中增加一大的自画Button,为预显位图之用,定义ID号为I_
view,如下图:
标准的File Open对话框:
图1
定制过的File Open对话框:
图2
注:变动如下:
(1)边框加长;
(2)删除"read only"项;
(3)移动"OK"、"Cancel"、"Help"按钮;
(4)增加一大自画按钮button1。
2.编写处理代码
VC++自动生成的程序框架已为Open命令提供了缺省的处理函数,
为定制该对话框就需重写Open命令的处理函数。用ClassWizard在文
档类中添加一个菜单处理函数OnOpenFile(),具体程序如下:
void CDibPreViewDoc::OnFileOpen()
{
//TODO:Add your command handler code here
char FileNameString[]="*.dib";
char FilterString[]="DIB file(*.dib)|*.dib|BMP file(*.bm
p)|*.bmp|All file(. *)|*.*||";
CFileDialog FileDialog(TRUE,NULL,(LPSTR)FileNameString,O
FN--HIDEREAD|OFN-OE RWRITEPROMPT,(LPSTR)FilterString);
FileDialog.m-ofn.Flags=OFN—SHOWHELP|OFN—ENABLEHOOK|OFN
—HIDEREADONLY|OFN—ENABLETEMPLATE;
FileDialog.m-ofn.lpfnHook=(LPOFNHOOKPROC)MakeProcInstanc
e(FileOpenHookProcN ULL);
FileDialog.m—ofn.lpTemplateName=(LPSTR)MAKEINTRESOURCE(
FILEOPENORD);
if(FileDialog.DoModal()==IDOK)
}
……
}
}
另外,为了定制CFileDialog的行为,我们定义一个钩子函数FileO
penHookProc()来截获发给CFileDialog的全部消息,并对其进行筛选,
选取自己需要进行处理。对自己不处理的消息或自己处理完后仍要交
给MFC处理的消息,钩子函数返回FALSE;对自己处理完后而不需要F C
处理的消息,钩子函数返回TRUE。由于篇幅所限,仅提供本例程序框架 。
BOOL CALLBACK FileOpenHookProc(
HWND hDlg,
/*window handle of the dialog box*/
UINT message, /*type of message */
WPARAM wParam, /*message_specific information */
LPARAM lParam)
{
switch(message)
{
case WM—INITDIALOG:
//初始化工作
……
break;
case WM—COMMAND:
//判断是否为文件列表框中选择结果发生改变时发出的消息
if(LOWORD(wParam)==lstl &&HIWORD(wParam)==LBN—SELCHA
NGE)
/* lstl是文件列表框的ID号*/
{
//获取列表框现行的选中项的序号
int nItem=SendDlgItemMessage(hDlg,lst1,LB—GETCURSEL,(WP
ARAM) 0,(LPARAM) 0
//获取列表框现行的选中项的字符串的长度
int tmpLength=SendDlgItemMessage(hDlg,ist1,LB—GETTEXTLE
N,(WPARAM) nItem,(P ARAM) 0);
//获取列表框现行的选中项的字符串
::SendDlgItemMessage(hDlg,lst1,LB—GETTEXT,(WPARAM) nI
tem,(LPARAM) &Filea me);
FileName[tmpLength]=0;
//获取增加的Button的窗口句柄
HWND staticItem=::GetDlgItem(hDlg,IDC—view);
if(!staticItem)
MessaageBox(hDlg,"Can not find HWND","Error",MB-OK);
//要求刷新增加的Button的窗口
::InvalidateRect(staticItem,NULL,TRUE);
::UpdateWindow(staticItem);
}
break;
case WM—DRAWITEM:
LPDRAWITEMSTRUC lpdis=(LPDRAWI TEMSTRUCT) lParam;
HDC hdcMem=lpdis—>hDC;
//判断是否为要求刷新buttonl窗口而发出的消息
if(wParam==IDC_view)
//IDC_view是新增加的buttonl的ID号
{
//刷新增加的Buttonl窗口(在buttonl上显示位图)
……
}
break;
}
return(FALSE);
}