您的位置:首页 > 其它

用VC6打开XP风格通用文件对话框

2008-10-16 13:30 323 查看
一般地,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_)

CFileDialogEx.cpp

// 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)所有;转载文章版权归原作者所有;

http://blog.csdn.net/boythl

欢迎转载,但请注明出处,保留作者和版权信息。

===================================
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: