一般地,VC6中使用CFileDialog来打开windows通用打开文件对话框和保存文件对话框。但,遗憾地是,很多人告诉我(包括我头头),也许也会这么告诉你,CFileDialog只能打开win98风格的对话框,如下图一。
图一,标准文件打开对话框
但我们是不会满足的,看着别人程序里漂亮的XP风格对话框,难道我们只能扩展CFileDialog,自己实现(所有我认识的人都是这么告诉我的)?
其实,只需简单的一步,就可以用VC6的CFileDialog来打开XP风格文件对话框,如图二,
图二 XP风格的通用文件对话框,带左侧"我的电脑"按钮
目前,在VC6.0中, 我知道有四种方法可以实现调用XP风格通用文件对话框:
一。设置CFileDialog的 m_ofn 成员变量的 lStructSize 值
- CFileDialog dlg(TRUE);
- dlg.m_ofn.lStructSize = 88;//76将是win98风格
但请注意,1. 不能设置 m_ofn.Flags 的 OFN_ENABLEHOOK 标志和 m_ofn.lpfnHook 变量,否则还将是98风格。2.这个方法好像只适合Win2000及winXP版本windows,不支持95和98.
这是因为,新的Open对话框是用一个新版本的commdlg.dll实现的,显示它的函数是GetOpenFileName,与在Windows 9x 和Windows NT下使用的相同。然而,GetOpenFileName现在使用一个新版本的OPENFILENAME;9x下该结构的size是76,很不幸,进入NT时代,它被扩展成88。另一方面,“如果OPENFILENAME有旧的大小,Windows 2000使用OFN_ENABLEHOOK来决定运行哪个对话框。如果OPENFILENAME使用hook过程(或者设置了ORN_ENABLETEMPLATE),Windows 2000按照旧的风格显示对话框;否则,显示新的对话框。”
二。设置m_ofn的Flags
使m_ofn.Flags值包含 OFN_EXPLORER | OFN_ALLOWMULTISELECT,但不能包含OFN_ENABLEHOOK ,否则将仍是旧风格对话框。
遗憾的是,因为不含有 OFN_ENABLEHOOK 标记,在 CFileDialog.DoModal()时,将会ASSERT,因为有如下判断:
- int CFileDialog::DoModal()
- {
- ASSERT_VALID(this);
- ASSERT(m_ofn.Flags & OFN_ENABLEHOOK);
- ASSERT(m_ofn.lpfnHook != NULL);
但因为是ASSERT,说明在release版本下是没问题的,仅Debug版下会ASSERT,当然,若你愿意,您也可以点忽略继续执行。
三。直接使用API函数GetOpenFileName
CFileDialog也是调用的SDK中的GetOpenFileName API函数,可以直接设置它的Flags,这个我未试过,请参考MSDN。
四。派生CFileDialog类
这个解决方案来自MSDN,http://msdn.microsoft.com/zh-cn/magazine/cc301412(en-us).aspx
由CFileDialog类 派生一个CFileDialogEx类,貌似该类可以在9x系统下弹出XP风格的对话框;遗憾地是,从我有记忆起,我仅高中阶段用过95/98;现在是找一个来测试都困难。。。
完整代码附录如下,编译测试通过:
- // FileDialogEx.h : header file
- //
- // Windows 2000 version of OPENFILENAME.
- // The new version has three extra members.
- // This is copied from commdlg.h
- //
- #if !defined(AFX_FILEDIALOGEX_H__A7F5E19C_B48A_481E_94D9_7922B9E2449E__INCLUDED_)
- #define AFX_FILEDIALOGEX_H__A7F5E19C_B48A_481E_94D9_7922B9E2449E__INCLUDED_
- #if _MSC_VER > 1000
- #pragma once
- #endif // _MSC_VER > 1000
- struct OPENFILENAMEEX : public OPENFILENAME {
- void * pvReserved;
- DWORD dwReserved;
- DWORD FlagsEx;
- };
- /
- // CFileDialogEx: Encapsulate Windows-2000 style open dialog.
- //
- class CFileDialogEx : public CFileDialog {
- DECLARE_DYNAMIC(CFileDialogEx)
- public:
- CFileDialogEx(BOOL bOpenFileDialog, // TRUE for open, FALSE for FileSaveAs
- LPCTSTR lpszDefExt = NULL,
- LPCTSTR lpszFileName = NULL,
- DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
- LPCTSTR lpszFilter = NULL,
- CWnd* pParentWnd = NULL);
- // override
- virtual int DoModal();
- protected:
- OPENFILENAMEEX m_ofnEx; // new Windows 2000 version of OPENFILENAME
- virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);
- // virtual fns that handle various notifications
- virtual BOOL OnFileNameOK();
- virtual void OnInitDone();
- virtual void OnFileNameChange();
- virtual void OnFolderChange();
- virtual void OnTypeChange();
- DECLARE_MESSAGE_MAP()
- //{{AFX_MSG(CFileDialogEx)
- //}}AFX_MSG
- };
- //{{AFX_INSERT_LOCATION}}
- // Microsoft Visual C++ will insert additional declarations immediately before the previous line.
- #endif // !defined(AFX_FILEDIALOGEX_H__A7F5E19C_B48A_481E_94D9_7922B9E2449E__INCLUDED_)
- // MSDN -- August 2000
- // If this code works, it was written by Paul DiLascia.
- // If not, I don't know who wrote it.
- // Largely based on original implementation by Michael POSThttp://expert.youkuaiyun.com/Expert/reply.C++ 6.0, runs on Windows 98 and probably NT too.
- //
- // CFileDialogEx implements a CFileDialog that uses the new Windows
- // 2000 style open/save dialog. Use companion class CDocManagerEx in an
- // MFC framework app.
- //
- #include "stdafx.h"
- #include <afxpriv.h>
- #include "FileDialogEx.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
- static BOOL IsWin2000();
- IMPLEMENT_DYNAMIC(CFileDialogEx, CFileDialog)
- CFileDialogEx::CFileDialogEx(BOOL bOpenFileDialog, LPCTSTR lpszDefExt,
- LPCTSTR lpszFileName, DWORD dwFlags, LPCTSTR lpszFilter, CWnd* pParentWnd) :
- CFileDialog(bOpenFileDialog, lpszDefExt, lpszFileName,
- dwFlags, lpszFilter, pParentWnd)
- {
- }
- BEGIN_MESSAGE_MAP(CFileDialogEx, CFileDialog)
- //{{AFX_MSG_MAP(CFileDialogEx)
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
- BOOL IsWin2000()
- {
- OSVERSIONINFOEX osvi;
- BOOL bOsVersionInfoEx;
- // Try calling GetVersionEx using the OSVERSIONINFOEX structure,
- // which is supported on Windows 2000.
- //
- // If that fails, try using the OSVERSIONINFO structure.
- ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
- {
- // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
- osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
- if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
- return FALSE;
- }
- switch (osvi.dwPlatformId)
- {
- case VER_PLATFORM_WIN32_NT:
- if ( osvi.dwMajorVersion >= 5 )
- return TRUE;
- break;
- }
- return FALSE;
- }
- //
- // DoModal override copied mostly from MFC, with modification to use
- // m_ofnEx instead of m_ofn.
- //
- int CFileDialogEx::DoModal()
- {
- ASSERT_VALID(this);
- ASSERT(m_ofn.Flags & OFN_ENABLEHOOK);
- ASSERT(m_ofn.lpfnHook != NULL); // can still be a user hook
- // zero out the file buffer for consistent parsing later
- ASSERT(AfxIsValidAddress(m_ofn.lpstrFile, m_ofn.nMaxFile));
- DWORD nOffset = lstrlen(m_ofn.lpstrFile)+1;
- ASSERT(nOffset <= m_ofn.nMaxFile);
- memset(m_ofn.lpstrFile+nOffset, 0, (m_ofn.nMaxFile-nOffset)*sizeof(TCHAR));
- // WINBUG: This is a special case for the file open/save dialog,
- // which sometimes pumps while it is coming up but before it has
- // disabled the main window.
- HWND hWndFocus = ::GetFocus();
- BOOL bEnableParent = FALSE;
- m_ofn.hwndOwner = PreModal();
- AfxUnhookWindowCreate();
- if (m_ofn.hwndOwner != NULL && ::IsWindowEnabled(m_ofn.hwndOwner))
- {
- bEnableParent = TRUE;
- ::EnableWindow(m_ofn.hwndOwner, FALSE);
- }
- _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
- ASSERT(pThreadState->m_pAlternateWndInit == NULL);
- if (m_ofn.Flags & OFN_EXPLORER)
- pThreadState->m_pAlternateWndInit = this;
- else
- AfxHookWindowCreate(this);
- memset(&m_ofnEx, 0, sizeof(m_ofnEx));
- memcpy(&m_ofnEx, &m_ofn, sizeof(m_ofn));
- if (IsWin2000())
- m_ofnEx.lStructSize = sizeof(m_ofnEx);
- int nResult;
- if (m_bOpenFileDialog)
- nResult = ::GetOpenFileName((OPENFILENAME*)&m_ofnEx);
- else
- nResult = ::GetSaveFileName((OPENFILENAME*)&m_ofnEx);
- memcpy(&m_ofn, &m_ofnEx, sizeof(m_ofn));
- m_ofn.lStructSize = sizeof(m_ofn);
- if (nResult)
- ASSERT(pThreadState->m_pAlternateWndInit == NULL);
- pThreadState->m_pAlternateWndInit = NULL;
- // WINBUG: Second part of special case for file open/save dialog.
- if (bEnableParent)
- ::EnableWindow(m_ofnEx.hwndOwner, TRUE);
- if (::IsWindow(hWndFocus))
- ::SetFocus(hWndFocus);
- PostModal();
- return nResult ? nResult : IDCANCEL;
- }
- //
- // When the open dialog sends a notification, copy m_ofnEx to m_ofn in
- // case handler function is expecting updated information in the
- // OPENFILENAME struct.
- //
- BOOL CFileDialogEx::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
- {
- memcpy(&m_ofn, &m_ofnEx, sizeof(m_ofn));
- m_ofn.lStructSize = sizeof(m_ofn);
- return CFileDialog::OnNotify( wParam, lParam, pResult);
- }
- // The following functions are provided for testing purposes, to
- // demonstrate that they in fact called; ie, that MFC's internal dialog
- // proc is hooked up properly. Delete them if you like.
- //
- BOOL CFileDialogEx::OnFileNameOK()
- {
- TRACE(_T("CFileDialogEx::OnFileNameOK/n"));
- return CFileDialog::OnFileNameOK();
- }
- void CFileDialogEx::OnInitDone()
- {
- TRACE(_T("CFileDialogEx::OnInitDone/n"));
- CFileDialog::OnInitDone();
- }
- void CFileDialogEx::OnFileNameChange()
- {
- TRACE(_T("CFileDialogEx::OnFileNameChange/n"));
- CFileDialog::OnFileNameChange();
- }
- void CFileDialogEx::OnFolderChange()
- {
- TRACE(_T("CFileDialogEx::OnFolderChange/n"));
- CFileDialog::OnFolderChange();
- }
- void CFileDialogEx::OnTypeChange()
- {
- TRACE(_T("OnTypeChange(), index = %d/n"), m_ofn.nFilterIndex);
- CFileDialog::OnTypeChange();
- }
然后直接调用CFileDialogEx类即可(VC6 SP6 + WinXP测试通过):
- CFileDialogEx d(TRUE);
- d.DoModal();
本文写作过程中查阅了大量资料,向这些同行们表示敬意和感谢!最后,骂一句优快云的Programmer,MD,贴两张图片都让我累死。FK。
===================================
非注明转载的文章和blog在未特殊声明情况下一般为本人原创或整理,
原创文章版权归沙漠孤狐(lonefox)所有;转载文章版权归原作者所有;
http://blog.youkuaiyun.com/boythl
欢迎转载,但请注明出处,保留作者和版权信息。
===================================