怎么样在MFC程序中显示win2000样式的文件打开对话框?
文件对话框是一种特定的窗口,对话框运行时对各种消息的响应是通过对话的“钩子函数”来完成的。对于对话框的外观,我们可以通过修改“文件对话框模板”来实现;同样,如果要想文件对话实现其它功能(如带文件预览功能的文件对话框),可以通过修改文件对话框的“钩子函数”来完成。在VC中,系统为我们提供了现成的文件对话框类CFileDialog,利用该类,我们可以很方便地生成的文件对话框。在使用CFileDialog时,其中最重要的工作是对其成员变量m_ofn做初始化工作。 |
m_ofn实际上是一OPENFILENAME类型的结构(Struct) ,OPENFILENAME的定义如下: |
DWORD lStructSize;// OPENFILENAME结构的大小 |
………………此处略去具体的成员变量……………………. |
#if (_WIN32_WINNT >= 0x0500) |
#endif // (_WIN32_WINNT >= 0x0500) |
} OPENFILENAME, *LPOPENFILENAME; |
由上可以看出,OPENFILENAME中有一Flags成员变量,它决定了对话框的外观,它是一组预定义宏的组合。通过它,我们可以定制个性化的文件对话框。比如,在图一中,“以只读方式打开”这一选项就是因为在Flags中包括了“OFN_READONLY”。 |
当我们在使用OPENFILENAME结构时,我们已经习惯了用下面的语句来设定lStructSize: |
dlgFileOpen.lStructSize=sizeof(OPENFILENAME); |
//注:无论是在Windows98或是Windows2000下运行,lStructSize的值只会是76 |
实际上,我们如果仔细算一下,76只是#if (_WIN32_WINNT >= 0x0500)前所有成员变量的长度,如果加上#if (_WIN32_WINNT >= 0x0500)以后的三个变量(pvReserved、dwReserved、FlagsEx)的长度, lSstructSize的值应该是76+3*4=88。正是由于这个习惯性的操作,我们才在无意中有意让Windows2000显示老式的文件对话框,原因是什么?请继续往下看: |
我们知道,Windows 2000的版本号已经突破了5。如果你使用_WIN32_WINNT = 0x0500进行程序的编译,#if (_WIN32_WINNT >= 0x0500)后的头两个成员参数被当成了保留值,剩下和一个成员参数FlagsEx,就有了一个新的标志值可供选择:OFN_EX_NOPLACESBAR,正是因为OFN_EX_NOPLACESBAR,你才不能够在你所编写的程序中见到快捷工具栏。奇怪的是,和OFN_EX_NOPLACESBAR相比,VC并未提供“OFN_EX_SHOWPLACESBAR”之类的选择。 |
之所以对lStructSize的值算来算去,是因为在Windows2000中使用MFC的CFileDialog所生成的对话框时,lStructSize的值直接影响着对文件对话框对快捷工具栏显示与否。如果OPENFILENAME的大小是76,则文件对话框不显示快捷工具栏;如果OPENFILENAME的大小是88,文件对话框显示快捷工具栏。当然,这种说法是有一定前提条件的,在以下的叙述中,你会对此有更深刻的了解。 |
如果Windows 2000 仅仅通过判别OPENFILENAME 的lStructSize值来确定显示文件对话框的方式,那么,为什么有些在Windows98 下运行的程序在Windows 2000下依然能够显示新型的文件对话框(如Windows98中的“记事本”程序)? 很明显,Windows98下的lStructSize值一定是76, 所以Windows 2000一定还有另一种方式来决定使用何种形式来显示文件对话框,是什么呢? |
OPENFILENAME结构中有一Flags值,这个值决定着文件对话框的外观。 Windows 2000就是通过这值来从另一方面来决定使用何种形式来显示文件对话框(准确地说,如果你使用的是MFC的CFileDialog所生成的对话框)是同时通过Flags和lStructSize来决定使用何种形式来显示文件对话框。如果Flags值包含了OFN_ENABLEHOOK(启用钩子函数),且lStructSize值是88,显示新的文件对话框;如果Flags不含OFN_ENABLEHOOK(注意:CFileDialog的Flags必须要含OFN_ENABLEHOOK),那么,不管lStructSize的值是多少,显示新的文件对话框;如果OPENFILENAME使用了对话框钩子函数,且lStructSize的值是76, Windows 2000 显示老式的文件对话框;这种显示机制解释了利用MFC的CFileDialog的应用程序在Windows 2000 下运行时总是显示老式的文件对话框。因为, MFC在文件对话框中都使用了对话框钩子函数,不信,你去看看CFileDialog类的实现源程序Dlgfile.cpp第76行:ASSERT(m_ofn.Flags & OFN_ENABLEHOOK),这从根本上说明,MFC的对话框必须使用钩子函数!而且,我们所习惯的“dlgFileOpen.lStructSize=sizeof(OPENFILENAME)”这种操作只会强制地要求Windows2000显示老式的文件对话框。这就注定了使用CFileDialog类的文件对话框在Windows 2000下必定被显示成老式的文件对话框。 |
问题很明显了,归纳一下上面所述内容,我们可以得出以下结论:1、如果是在Windows98下,你用MFC的CFileDialog类生成的对话框是不能实现新式对话框的显示。也许你会问,Windows98中运行Office2000时,系统照样能够显示新型的文件对话框,笔者在此做猜测,那是因为Office2000的文件对话框根本未使用MFC的对话显示原理,如果非要在Windows98中显示新的文件对话框,你得重新定义一个文件对话框类。2、显然,并不是所有的文件对话框程序都是由MFC的CFileDialog所生成的。如果你不用CFileDialog对话框,你根本不用考虑文件对话框的外观,新产生的对话框外观将自动随操作系统而自动地在新式与老式之间进行转换。3、在Windows 2000下,使用MFC生成的对话框,如果你能够确认文件对话框未使用对话框钩子函数(如GetOpenFileName()函数),那么,你开发的程序一定能够显示出新的对话框(而不必在乎OPENFILENAME中lStructSizer的值),而且这种程序在使用时不必在乎运行的操作系统平台,这也不难理解 “记事本”程序在Windows98和Windows2000下运行时会有不同的文件对话框界面了;如果MFC文件对话框使用钩子函数,那么,有且仅有的办法是修改OPENFILENAME的大小,当OPENFILENAME的大小是88时,Window2000显示新型的文件对话框,否则显示老式的文件对话框。同时要提到的是:将OPENFILENAME的值强行设为88,这样的程序在Windows98下面是不能正常运行的(你根本见不到对话框),你得在程序中加入对应用程序的运行平台的判断代码了。 |
有了上述的认识,我们也可以利用MFC做出适应系统平台的文件对话框了,正如“写字板”那样:在Windows98下面显示老式的文件对话框,而在Windows2000中,则显示新型的文件对话框。试试下面的程序: |
在VC中新建一单文档(SDI)项目,在资源编辑器中分别加入两项菜单项:“用CFileDialog产生对话框”和“用GetOpenFileName产生对话框”,同时加入对该两项菜单COMMAND事件的响应函数,如下: |
void CMainFrame::OnCfiledialog() |
CFileDialog dlgFileOpen(TRUE); |
DWORD dwVersion,dwWindowsMajorVersion,dwWindowsMinorVersion; |
//检测目前的操作系统,GetVersion具体用法详见MSDN |
dwVersion = GetVersion(); |
dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); |
dwWindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); |
// 如果运行的操作系统是Windows NT/2000 |
if (dwVersion < 0x80000000) |
structsize =88;//显示新的文件对话框 |
structsize =76;//显示老的文件对话框 |
dlgFileOpen.m_ofn.lStructSize=structsize; |
TCHAR lpstrFilename[MAX_PATH] = ""; |
dlgFileOpen.m_ofn.lpstrFile=lpstrFilename; |
if(dlgFileOpen.DoModal()==IDOK) |
MessageBox("你所打开的文件是:"+(CString)dlgFileOpen.m_ofn.lpstrFile); |
void CMainFrame::OnGetopenfilename() |
TCHAR lpstrFilename[MAX_PATH] = ""; |
ZeroMemory(&ofn, sizeof(ofn)); |
ofn.lStructSize = sizeof(OPENFILENAME);//lStructSize的返回值是是76 |
ofn.hwndOwner = this->m_hWnd; |
ofn.lpstrFilter = "所有的文件/0*.*/0文本文件/0*.TXT/0"; |
ofn.lpstrFile=lpstrFilename; |
if (GetOpenFileName(&ofn)) |
MessageBox("你所打开的文件是:"+(CString)ofn.lpstrFile); |
转自:http://www.chinaitpower.com/A/2001-10-04/734.html
其于以上分析,我在VC中是这样使用的:
char szFileFilter[]=
"All File(*.*)|*.*|"
"Windows Media File(*.asf;*.wm;*.wma;*.wmv;*.wmd)|*.asf;*.wm;*.wma;*.wmv;*.wmd|"
"Windows 视频文件(*.avi;*.wmv)|*.avi;*.wmv|"
"Windows 音频文件(*.wma;*.wav;*.mp3;*.snd;*.au)|*.wma;*.wav;*.mp3;*.snd;*.au|"
"Windows 播放列表(*.asx;*.wax;*.m3u;*.wpl;*.wvx;*.wmx)|*.afx;*.wax;*.m3u;*.wpl;*.wvx;*.wmx|"
"Midi File(*.mid;*,rmi;*.midi)|*.mid;*.rmi;*.midi|"
"Movie File(*.mpg;*.mpeg;*.m1v;*.mp2;*.mpa;*.mpe)|*.mpg;*.mpeg;*.m1v;*.mp2;*.mpa;*.mpe|"
"RM File(*.rm;*.rmvb)|*.rm;*.rmvb||";
CFileDialog dlg(TRUE,NULL,NULL,OFN_HIDEREADONLY,szFileFilter);
int structsize=0;
DWORD dwVersion,dwWindowsMajorVersion,dwWindowsMinorVersion;
/** 检测目前的操作系统,GetVersion具体用法详见MSDN */
dwVersion = GetVersion();
dwWindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
dwWindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
/** 如果运行的操作系统是Windows NT/2000 */
if (dwVersion < 0x80000000)
{
structsize =88; /** NT系统, 显示新的文件对话框 */
}
else
{
structsize =76; /** Windows 95/98系统, 显示老的文件对话框 */
}
dlg.m_ofn.lStructSize = structsize;
if(dlg.DoModal()==IDOK)
{
m_strPathName = dlg.GetPathName();
CDialog::OnOK();
}
else
{
m_strPathName = "";
CDialog::OnCancel();
}