引言
通常情况下,利用vc为应用程序创建对话框需要使用对话框模板资源。通过vc++集成开发环境中的对话框编辑器以可视化的方式创建对话框资源,这种方法可以应用于大多数对话框编程的情况下。但涉及对话框的程序模块必然依赖于开发项目资源,不能作为多个开发项目共享的公用程序模块。比如,笔者开发的一系列应用程序,都需要一个用户输入注册信息的功能。通常情况下,可以将公用程序模块引入不同的开发项目,而实际程序文件只有唯一的备份。这样便于程序的维护,避免公用模块存在多个不一致拷贝的情况。但如果在公用程序模块中存在用户界面,则不能简单引入不同开发项目(因为不同项目都有各自的资源,其资源定义不免存在冲突)。另外如果开发过程中需要使用大量的对话框,这些对话框内容相似,但又略有不同,用对话框模板资源,也显得较为烦琐。本文介绍了在Visualc++中不使用常规的对话框资源模板的情况下,动态创建对话框的方法。
1、基本原理
如何不使用对话框模板资源,创建对话框应用,正是本文探讨的问题。如果仔细阅读MSDN中有关CDialog的内容会发现
CDialogCreateIndriect和
CDialogInitModalIndirect两个成员函数。正是这两个成员函数可以利用内存中的对话框资源创建对话框。
CDialogCreateIndriect用于创建无模式对话框,
CDialogInitModalIndirect可以创建模式对话框。
这两个成员函数都包含参数
LPCDLGTEMPLATElpDialogTemplate,它指向包含对话框和控件资源的内存位置。该段内存的具体内容包括两部分:
1.1对话框资源
最开始为DLGTEMPLATE结构,指出对话框窗口的位置、大小和风格,紧接其后的是指向菜单资源的数组(前两个字节为0x0000表示没有菜单),
接下来的UniCode字符串数组表示控件对应的注册类名(前两个字节为0x0000表示使用缺省的预定义类),然后是UniCode字符串数组表示的对话框标题,双字节的字体长度和字体名。
1.2对话框控件资源
紧跟对话框资源之后的是一个或多个对话框控件资源,每个控件资源最开始为DLGITEMTEMPLATE结构,指出控件窗口的位置、大小和风格,然后是控件对应的的注册类名,(如果前两个字节为0xFFFF表示使用预定义类),此时后两个字节指出具体类见表1,然后是控件标题(如果前两个字节为0xFFFF表示将使用ICON资源),接下来的数据用于创建控件时使用,由系统发送WM_CREATE消息时,被lParam参数引用,可以用0x0000表示省略该部分数据。
|
|
0x0080 | Button |
0x0081 | Edit |
0x0082 | Static |
0x0083 | List box |
0x0084 | Scroll bar |
0x0085 | Combo box |
表1
其中DLGTEMPLATE结构为
typedefstruct//dltt
DWORDstyle
DWORDdwExtendedStyle
WORD cdit
shortx
shorty
shortcx
shortcy
DLGTEMPLATE
style和dwExtendedStyle为对话框的风格,cdit表示对话框包含的控件个数,x,y,cx,cy表示窗口的大小。DLGITEMTEMPLATE结构与此类似,仅多了一项标识id。
通过上面的描述,可以知道只须自行申请内存空间,按要求在内存中定义对话框和控件的资源,就可以不再依赖项目资源。实际编程中,因为在对话框类中定义和创建控件比较容易,因此往往可以仅定义内存对话框资源而省略控件资源部分,通过常规方法动态创建就可以了。
2、实现方法与步骤
下面结合一个进度显示对话框实例介绍具体实现的步骤:
首先从CDialog中派生一个类CMemoryDialog,作为所有无模板对话框的基类。选择菜单Insert->NewClass弹出New Class对话框,在Name项键入CMemoryDialog,BaseClass项选择
CDialog,按确定按钮。ClassWizard提示用户首先创建对话框模板资源,对此不与理会,继续创建。在创建成功的.
h文件中找到enum IDD = _UNKNOWN_RESOURCE_ID_ ,表示未知的资源ID将这行代码删除。
定义成员变量:
CStringm_StrTitle//对话框标题
DLGTEMPLATEm_dlgTempl//内存对话框模板
定义成员函数:
intDoModalMemoryDialog//用于创建模式对话框
voidCreateMemoryDialog//用于创建无模式对话框
其中DoModalMemoryDialog的代码清单如下:
intCMemoryDialogDoModalMemoryDialog
intr
WCHAR szFontName = L″ARIAL″ //字体名
WCHAR pszBoxCaption //标题
int nLenTitle=m_StrTitle.GetLength+1
pszBoxCaption = new WCHARnLenTitle
//将标题转换为Unicode字符串
MultiByteToWideCharCP_ACP0m_StrTitle-1 pszBoxCaptionnLenTitle
TRY
//分配保存资源的内存
intnBufferSize=
sizeofDLGTEMPLATE+2 sizeofWORD/menu and class/ + nLenTitlesizeofWCHAR
nBufferSize+=sizeofWORD+sizeofszFontName/fontinformation/ +
nBufferSize=nBufferSize+2&2//adjustsize to make first control DWORD aligned
HLOCALhLocal
hLocal = LocalAllocLHND nBufferSize
if hLocal == NULL
AfxThrowMemoryException
BYTE pBuffer=BYTELocalLockhLocal
if pBuffer == NULL
LocalFreehLocal
AfxThrowMemoryException
BYTE pdest=pBuffer
// 拷贝DLGTEMPLATE 结构
memcpypdest &m_dlgTempl sizeofDLGTEMPLATE
pdest+=sizeofDLGTEMPLATE
WORDpdest=0//表示没有菜单
WORDpdest+1=0//使用缺省类
pdest+=2sizeofWORD
//拷贝标题字符串
memcpypdestpszBoxCaptionnLenTitlesizeofWCHAR
pdest+=nLenTitlesizeofWCHAR
WORDpdest=11//字体名的长度
pdest+=sizeofWORD
//拷贝字体名字符串
memcpypdestszFontNamesizeofszFontName
pdest+=sizeofszFontName
delete pszBoxCaption
//创建模式对话框
InitModalIndirectDLGTEMPLATEpBuffer
r=DoModal
LocalUnlockhLocal
LocalFreehLocal
CATCHCMemoryExceptione
AfxMessageBox″Memory allocation failed″
END_CATCH
return r
CreateMemoryDialog与此类似,只需将
InitModalIndirectDLGTEMPLATEpBuffer
r=DoModal
两行替换为
CreateIndirectDLGTEMPLATEpBuffer
ShowWindowSW_SHOW
并且去掉返回值。
然后从CMemoryDialog再次派生新类CCustomProgressDialog。在.h文件中加入成员CProgressCtrlm_ProCtrl,并覆盖基类的OnInitDialog成员函数。在OnInitDialog中动态创建CProgressCtrl对象。
这样一个简单的进度显示对话框就建立好了,用户可以根据需要派生更多的对话框类,也可随时添加更多的控件对象。
3、结论
通过上述进度显示对话框的实现步骤,可以看到建立无模板资源对话框的实现方法。用户可以根据需要派生更多的对话框类,也可随时添加更多的控件对象。而且这些模块都与资源无关,可以同时被多个开发项目包含,维护起来十分方便。