一般地,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.csdn.net/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();
本文寫作過程中查閱了大量資料,向這些同行們表示敬意和感謝!最後,罵一句CSDN的Programmer,MD,貼兩張圖片都讓我累死。FK。
===================================
非註明轉載的文章和blog在未特殊聲明情況下一般爲本人原創或整理,
原創文章版權歸沙漠孤狐(lonefox)所有;轉載文章版權歸原作者所有;
歡迎轉載,但請註明出處,保留作者和版權信息。
===================================