场景
-
之前在文章选择目录对话框里介绍了打开对话框选择文件夹的方法. 但是这两种方式总也有些小问题. 不建议使用了。
-
如果选择文件和文件夹都需要那么麻烦的话,
WTL
界面库按道理来说应该会集成了这类工具类.
说明
-
在
WTL
库里其实就有两个类来选择文件或文件夹CFileDialog
和CFolderDialog
, 在MFC
里有CFileDialog
和CFolderPickerDialog
相对应. -
WTL
里选择单个文件使用CFileDialog
, 选择多个文件是CMultiFileDialog
,当然CMultiFileDialog
是CFileDialog
的子类,在<atldlgs.h>
文件里.WTL
是开源的, 也可以看到类里其实就是对Win32
的GetOpenFileName
或GetSaveFileName
的调用,复杂的是对参数OPENFILENAME
的初始化.
CFileDialogImpl(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs
LPCTSTR lpszDefExt = NULL,
LPCTSTR lpszFileName = NULL,
DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
LPCTSTR lpszFilter = NULL,
HWND hWndParent = NULL)
{
memset(&m_ofn, 0, sizeof(m_ofn)); // initialize structure to 0/NULL
m_szFileName[0] = _T('\0');
m_szFileTitle[0] = _T('\0');
m_bOpenFileDialog = bOpenFileDialog;
#if defined(__AYGSHELL_H__) && (_WIN32_WCE >= 0x0501)
m_ofn.lStructSize = bOpenFileDialog ? sizeof(m_ofn) : sizeof(OPENFILENAME);
#else
m_ofn.lStructSize = sizeof(m_ofn);
#endif
#if (_WIN32_WINNT >= 0x0500)
// adjust struct size if running on older version of Windows
if(AtlIsOldWindows())
{
ATLASSERT(sizeof(m_ofn) > OPENFILENAME_SIZE_VERSION_400); // must be
m_ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
}
#endif // (_WIN32_WINNT >= 0x0500)
m_ofn.lpstrFile = m_szFileName;
m_ofn.nMaxFile = _MAX_PATH;
m_ofn.lpstrDefExt = lpszDefExt;
m_ofn.lpstrFileTitle = (LPTSTR)m_szFileTitle;
m_ofn.nMaxFileTitle = _MAX_FNAME;
#ifndef _WIN32_WCE
m_ofn.Flags = dwFlags | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING;
#else // CE specific
m_ofn.Flags = dwFlags | OFN_EXPLORER | OFN_ENABLEHOOK;
#endif // !_WIN32_WCE
m_ofn.lpstrFilter = lpszFilter;
m_ofn.hInstance = ModuleHelper::GetResourceInstance();
m_ofn.lpfnHook = (LPOFNHOOKPROC)T::StartDialogProc;
m_ofn.hwndOwner = hWndParent;
// setup initial file name
if(lpszFileName != NULL)
SecureHelper::strncpy_x(m_szFileName, _countof(m_szFileName), lpszFileName, _TRUNCATE);
}
INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow())
{
ATLASSERT((m_ofn.Flags & OFN_ENABLEHOOK) != 0);
ATLASSERT(m_ofn.lpfnHook != NULL); // can still be a user hook
ATLASSERT((m_ofn.Flags & OFN_EXPLORER) != 0);
if(m_ofn.hwndOwner == NULL) // set only if not specified before
m_ofn.hwndOwner = hWndParent;
ATLASSERT(m_hWnd == NULL);
#if (_ATL_VER >= 0x0800)
// Allocate the thunk structure here, where we can fail gracefully.
BOOL bRetTh = m_thunk.Init(NULL, NULL);
if(bRetTh == FALSE)
{
::SetLastError(ERROR_OUTOFMEMORY);
return -1;
}
#endif // (_ATL_VER >= 0x0800)
ModuleHelper::AddCreateWndData(&m_thunk.cd, (ATL::CDialogImplBase*)this);
BOOL bRet;
if(m_bOpenFileDialog)
#if defined(__AYGSHELL_H__) && (_WIN32_WCE >= 0x0501)
bRet = ::GetOpenFileNameEx(&m_ofn);
else
bRet = ::GetSaveFileName((LPOPENFILENAME)&m_ofn);
#else
bRet = ::GetOpenFileName(&m_ofn);
else
bRet = ::GetSaveFileName(&m_ofn);
#endif
m_hWnd = NULL;
return bRet ? IDOK : IDCANCEL;
}
CFolderDialog
是选择文件夹的工具类, 界面也是标准界面,看代码主要是对SHBrowseForFolder
的调用,最重要的也是调用参数的初始化. 想要定制复杂的文件夹选择类也是可以的, 不过需要自己继承并实现CDialogImpl
.
CFolderDialogImpl(HWND hWndParent = NULL, LPCTSTR lpstrTitle = NULL, UINT uFlags = BIF_RETURNONLYFSDIRS) :
m_lpstrInitialFolder(NULL), m_pidlInitialSelection(NULL), m_bExpandInitialSelection(false), m_pidlSelected(NULL), m_hWnd(NULL)
{
memset(&m_bi, 0, sizeof(m_bi)); // initialize structure to 0/NULL
m_bi.hwndOwner = hWndParent;
m_bi.pidlRoot = NULL;
m_bi.pszDisplayName = m_szFolderDisplayName;
m_bi.lpszTitle = lpstrTitle;
m_bi.ulFlags = uFlags;
m_bi.lpfn = BrowseCallbackProc;
m_bi.lParam = (LPARAM)static_cast<T*>(this);
m_szFolderPath[0] = 0;
m_szFolderDisplayName[0] = 0;
}
INT_PTR DoModal(HWND hWndParent = ::GetActiveWindow())
{
if(m_bi.hwndOwner == NULL) // set only if not specified before
m_bi.hwndOwner = hWndParent;
// Clear out any previous results
m_szFolderPath[0] = 0;
m_szFolderDisplayName[0] = 0;
::CoTaskMemFree(m_pidlSelected);
INT_PTR nRet = IDCANCEL;
m_pidlSelected = ::SHBrowseForFolder(&m_bi);
if(m_pidlSelected != NULL)
{
nRet = IDOK;
// If BIF_RETURNONLYFSDIRS is set, we try to get the filesystem path.
// Otherwise, the caller must handle the ID-list directly.
if((m_bi.ulFlags & BIF_RETURNONLYFSDIRS) != 0)
{
if(::SHGetPathFromIDList(m_pidlSelected, m_szFolderPath) == FALSE)
nRet = IDCANCEL;
}
}
return nRet;
}
例子
void CWTLSchoolView::SelectOneFile(HWND hwnd)
{
OutputDebugString(L"SelectOneFile \n");
CFileDialog filedialog(TRUE);
if(IDOK == filedialog.DoModal(hwnd)){
std::wstring path = filedialog.m_szFileName;
OutputDebugString((path+L"\n").c_str());
}
}
void CWTLSchoolView::SelectMultiFiles(HWND hwnd)
{
OutputDebugString(L"SelectMultiFiles \n");
CMultiFileDialog dialog;
if(IDOK == dialog.DoModal(hwnd)){
CString path;
bool result = dialog.GetFirstPathName(path);
while(result){
OutputDebugString((path+L"\n"));
result = dialog.GetNextPathName(path);
}
}
}
void CWTLSchoolView::SelectOneFolder(HWND hwnd)
{
OutputDebugString(L"SelectOneFolder \n");
CFolderDialog folder(NULL,L"Select a dir",BIF_RETURNONLYFSDIRS|BIF_USENEWUI);
if(IDOK == folder.DoModal()){
std::wstring dir = folder.m_szFolderPath;
OutputDebugString((dir+L"\n").c_str());
}
}
输出
选择文件夹
只能选择一个文件
选择多个文件