以选择文件夹对话框为例,研究C++11新特性。
调用Windows API SHBrowseForFolder 可以打开该对话框,进行浏览和选择对话框。调用该接口之前,需要构造一个结构体BROWSEINFO 。参考下面的函数:
- // hParent,父窗口; strFolder,返回选择的文件夹路径。
- BOOL WINAPI SelectFolder(HWND hParent, CString &strFolder)
- {
- BOOL bRet = FALSE;
- LPMALLOC lpMalloc;
- if (::SHGetMalloc(&lpMalloc) != NOERROR)
- {
- return FALSE;
- }
- TCHAR szDisplayName[_MAX_PATH];
- TCHAR szBuffer[_MAX_PATH];
- BROWSEINFO browseInfo;
- browseInfo.hwndOwner = hParent;
- browseInfo.pidlRoot = NULL;
- browseInfo.pszDisplayName = szDisplayName;
- browseInfo.lpszTitle = _T("请选择一个文件夹:\nPlease select a folder:");
- browseInfo.ulFlags = BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS;
- browseInfo.lpfn = NULL;
- browseInfo.lParam = NULL;
- LPITEMIDLIST lpItemIDList;
- if ((lpItemIDList = ::SHBrowseForFolder(&browseInfo)) != NULL)
- {
- if (::SHGetPathFromIDList(lpItemIDList, szBuffer))
- {
- if (szBuffer[0] != ‘\0‘)
- {
- strFolder = szBuffer;
- bRet = TRUE;
- }
- }
- lpMalloc->Free(lpItemIDList);
- lpMalloc->Release();
- }
- return bRet;
- }
但是,这样没有“新建文件夹”的按钮,也没有选择文件夹的编辑框显示,如下图。
如果需要新建文件夹的按钮,需要给BROWSEINFO的表示位ulFlags添加一个标志BIF_USENEWUI 。这个标志在VC6.0没有给出,可以自己定义:
#ifndef BIF_USENEWUI
#define BIF_NEWDIALOGSTYLE 0x00000040 // Use the new dialog layout with the ability to resize
#define BIF_USENEWUI (BIF_NEWDIALOGSTYLE | BIF_EDITBOX) // Caller needs to call OleInitialize() before using this API
#endif
然后把上面函数稍加修改, browseInfo.ulFlags = BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS|BIF_USENEWUI; 这样就有了“新建文件夹”按钮了。
但是,这样仍不完美。因为每次打开该对话框时,都是默认选择同样的目录,想要找到自己想要的目的文件夹就要多次点击鼠标才行。如果能够指定一个默认选择的文件夹就好了。而幸运的是,Windows提供了这样的接口。但需要通过回调的方式实现,如下:
- int CALLBACK BrowserCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
- {
- switch(uMsg)
- {
- case BFFM_INITIALIZED: //设置默认选择的文件夹
- ::SendMessage(hWnd, BFFM_SETSELECTION, 1, lpData);
- break;
- default:
- break;
- }
- return 0;
- }
- // hParent,父窗口; strFolder,输入默认选择的文件夹路径,返回新选择的文件夹路径。
- BOOL WINAPI SelFolder(HWND hParent, CString &strFolder)
- {
- BOOL bRet = FALSE;
- LPMALLOC lpMalloc;
- if (::SHGetMalloc(&lpMalloc) != NOERROR)
- {
- return FALSE;
- }
- TCHAR szDisplayName[_MAX_PATH];
- TCHAR szBuffer[_MAX_PATH];
- BROWSEINFO browseInfo;
- browseInfo.hwndOwner = hParent;
- browseInfo.pidlRoot = NULL;
- browseInfo.pszDisplayName = szDisplayName;
- browseInfo.lpszTitle = _T("请选择一个文件夹:\n\nPlease select a folder:");
- browseInfo.ulFlags = BIF_RETURNFSANCESTORS|BIF_RETURNONLYFSDIRS|BIF_USENEWUI;
- browseInfo.lpfn = BrowserCallbackProc;
- browseInfo.lParam = (LPARAM)(const char*)strFolder;
- LPITEMIDLIST lpItemIDList;
- if ((lpItemIDList = ::SHBrowseForFolder(&browseInfo)) != NULL)
- {
- if (::SHGetPathFromIDList(lpItemIDList, szBuffer))
- {
- if (szBuffer[0] != ‘\0‘)
- {
- strFolder = szBuffer;
- bRet = TRUE;
- }
- }
- lpMalloc->Free(lpItemIDList);
- lpMalloc->Release();
- }
- return bRet;
- }
这样就能达到想要的效果。
但是仍不完美,一个功能本来应该一个函数就实现的,却写了两个函数,看起来真是膈应。复制的时候,仅复制下面的函数是用不了的,程序的可读性、可移植性都大打折扣。如果能写在同一个函数中,再精简一下就完美了。VC6.0是不行了,VS2010也力不从心。
而VS2013用几个C++11的新特性就能轻松是实现: