VC編程中經常需要彈出選擇文件夾對話框供用戶選擇目標文件夾
-----------------------------------------------------------------------------------------------------------------------------------
一種是用MFC提供的CFileDiglog類
(vs2010環境)
#include "stdafx.h"
#include <Windows.h>
#include "BaseFunc.h"
using namespace std;
using namespace BaseFunc;
unsigned BaseFunc::selFile( string &strFile,const string &strExt,bool bOpen )
{
string strDir = "D:\\Downloads";//這里通過strFile解析目錄,CFileDialog會自動記住
string filename = "hi.txt"; //通過strFile解析文件名
string filter = strExt + "文件 (*." + strExt + ")|*." + strExt + "||";
string ext = "." + strExt;
CFileDialog dlg(bOpen,ext.c_str(),filename.c_str(),OFN_READONLY|OFN_OVERWRITEPROMPT,filter.c_str(),NULL);
dlg.GetOFN().lpstrInitialDir = strFile.c_str();// 默認目錄
if (dlg.DoModal())
{
strFile = dlg.GetPathName();
return IDOK;
}
return IDCANCEL;
}
-----------------------------------------------------------------------------------------------------------------------------------
另一種是通過SHBrowseForFolder函數,可以通過回調函數設置標題及路徑
基本實現的源碼如下
- void CTestDlg::OnBtnTest()
- {
- // TODO: Add your control notification handler code here
- TCHAR pszPath[MAX_PATH];
- BROWSEINFO bi;
- bi.hwndOwner = this->GetSafeHwnd();
- bi.pidlRoot = NULL;
- bi.pszDisplayName = NULL;
- bi.lpszTitle = TEXT("請選擇文件夾");
- bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
- bi.lpfn = NULL;
- bi.lParam = 0;
- bi.iImage = 0;
- LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
- if (pidl == NULL)
- {
- return;
- }
- if (SHGetPathFromIDList(pidl, pszPath))
- {
- AfxMessageBox(pszPath);
- }
- }
這一般均能夠滿足要求,但有時還是需要在此基礎上增強一些功能。
比如在彈出選擇文件夾對話框時選中默認的文件夾,或在STATUSTEXT區域顯示一些信息等等。這需要在BrowseCallbackProc回調函數中實現。具體實現的源碼如下:
(具體BrowseCallBackFun回調函數的用法請參照MSDN)
- //選擇文件夾對話框回調函數
- int CALLBACK BrowseCallBackFun(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
- {
- switch(uMsg)
- {
- case BFFM_INITIALIZED: //選擇文件夾對話框初始化
- //設置默認路徑為lpData即'D:\'
- ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
- //在STATUSTEXT區域顯示當前路徑
- ::SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, lpData);
- //設置選擇文件夾對話框的標題
- ::SetWindowText(hwnd, TEXT("請先設置個工作目錄"));
- break;
- case BFFM_SELCHANGED: //選擇文件夾變更時
- {
- TCHAR pszPath[MAX_PATH];
- //獲取當前選擇路徑
- SHGetPathFromIDList((LPCITEMIDLIST)lParam, pszPath);
- //在STATUSTEXT區域顯示當前路徑
- ::SendMessage(hwnd, BFFM_SETSTATUSTEXT, TRUE, (LPARAM)pszPath);
- }
- break;
- }
- return 0;
- }
- void CTestDlg::OnBtnTest()
- {
- // TODO: Add your control notification handler code here
- TCHAR pszPath[MAX_PATH];
- BROWSEINFO bi;
- bi.hwndOwner = this->GetSafeHwnd();
- bi.pidlRoot = NULL;
- bi.pszDisplayName = NULL;
- bi.lpszTitle = TEXT("請選擇文件夾");
- bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;
- bi.lpfn = BrowseCallBackFun; //回調函數
- bi.lParam = (LPARAM)TEXT("D:\\"); //傳給回調函數的參數,設置默認路徑
- bi.iImage = 0;
- LPITEMIDLIST pidl = SHBrowseForFolder(&bi);
- if (pidl == NULL)
- {
- return;
- }
- if (SHGetPathFromIDList(pidl, pszPath))
- {
- AfxMessageBox(pszPath);
- }
- }
附圖片:
當然也可以設置選擇文件對話框的其他樣式,比如使其具有新增文件夾的功能,可如下實現
bi.ulFlags = BIF_USENEWUI
附圖:(改圖來源於www.VCKBASE.com,本人電腦上安裝的是VC6,不支持BIF_USENEWUI,請在VC2003+上嘗試)
具體請參照MSDN
定義
結構
成員變量
------------------------------------------------------
若要建立可以新建文件夾的對話框:
在CPP開頭加上:
#define BIF_NEWDIALOGSTYLE 0x40
#define BIF_USENEWUI (BIF_NEWDIALOGSTYLE|BIF_EDITBOX)
在風格中多加上 BIF_USENEWUI
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT |BIF_USENEWUI;
彈出指本地特殊指定的文件夾窗口,類似我們雙擊某個文件夾打開
文件目錄轉成LPITEMIDLIST
當用目錄選擇對話框時,可以用如下的結構打開。
typedef struct _browseinfo {
HWND hwndOwner; // 父窗口句柄
LPCITEMIDLIST pidlRoot; // 要顯示的文件夾的根(Root)
LPTSTR pszDisplayName; // 保存被選取的文件夾路徑的緩沖區
LPCTSTR lpszTitle; // 顯示位於對話框左上部的標題
UINT ulFlags; // 指定對話框的外觀和功能的標志
BFFCALLBACK lpfn; // 處理事件的回調函數
LPARAM lParam; // 應用程序傳給回調函數的參數
int iImage; // 保存被選取的文件夾的圖片索引
} BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO;
LPCITEMIDLIST pidlRoot; 這個就是 默認為桌面。
還有可以通過這種方式實現,如下:
LPMALLOC pMalloc;
if ( SUCCEEDED( SHGetSpecialFolderLocation (NULL, CSIDL_DRIVES, &pidl)))
{
SHELLEXECUTEINFO sei;
ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_IDLIST;
sei.lpIDList = pidl;
sei.lpVerb = "open";
sei.hwnd = AfxGetMainWnd()->GetSafeHwnd();
sei.nShow = BIF_RETURNONLYFSDIRS;
ShellExecuteEx(&sei);
if (SUCCEEDED( SHGetMalloc (&pMalloc)))
{
pMalloc->Free ( pidl );
pMalloc->Release();
}
}
其中://CSIDL_DRIVES是我的電腦
參數有好些,
CSIDL_BITBUCKET 回收站
CSIDL_CONTROLS 控制面板
CSIDL_DESKTOP Windows 桌面Desktop
CSIDL_DESKTOPDIRECTORY Desktop的目錄
CSIDL_DRIVES 我的電腦
CSIDL_FONTS 字體目錄
CSIDL_NETHOOD 網上鄰居
CSIDL_NETWORK 網上鄰居虛擬目錄
CSIDL_PERSONAL 我的文檔
CSIDL_PRINTERS 打印機
CSIDL_PROGRAMS 程序組
CSIDL_RECENT 最近打開的文檔
CSIDL_SENDTO “發送到”菜單項
CSIDL_STARTMENU 任務條啟動菜單項
CSIDL_STARTUP 啟動目錄
CSIDL_TEMPLATES 文檔模板
其他的參數可以查閱MSDN。
現在像論壇上的朋友,他要實現的是要打開 如:f:,f:\\site等這樣的目錄。
很明顯這樣的字符串是不被支持的,所以必須要轉成 LPCITEMIDLIST 這種結構的才支持。
但微軟好像沒有類似的函數實現這樣的功能。於是就寫了一個函數如下:
//文件目錄轉成LPITEMIDLIST
LPITEMIDLIST CTestBrowseDlg::ParsePidlFromPath(LPCSTR path)
{
OLECHAR szOleChar[MAX_PATH];
LPSHELLFOLDER IpsfDeskTop;
LPITEMIDLIST lpifq;
ULONG ulEaten, ulAttribs;
HRESULT hres;
SHGetDesktopFolder(&IpsfDeskTop);
MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,path,-1,szOleChar,sizeof(szOleChar));
hres = IpsfDeskTop ->ParseDisplayName(NULL, NULL, szOleChar, &ulEaten, &lpifq, &ulAttribs);
hres=IpsfDeskTop->Release( );
if(FAILED(hres))
return NULL;
return lpifq;
}
如這樣寫:
方法1:
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
pidl = ParsePidlFromPath("F:\\site");//請先確定f:下有這個文件夾
SHELLEXECUTEINFO sei;
ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_IDLIST;
sei.lpIDList = pidl;
sei.lpVerb = "open";
sei.hwnd = AfxGetMainWnd()->GetSafeHwnd();
sei.nShow = BIF_RETURNONLYFSDIRS;
ShellExecuteEx(&sei);
if (SUCCEEDED( SHGetMalloc (&pMalloc)))
{
pMalloc->Free ( pidl );
pMalloc->Release();
}
就可以打開F:\site
方法2:
CString str;
BROWSEINFO bi;
char name[MAX_PATH];
ZeroMemory(&bi,sizeof(BROWSEINFO));
bi.pidlRoot = ParsePidlFromPath("F:\");
bi.hwndOwner=GetSafeHwnd();
bi.pszDisplayName=name;
bi.lpszTitle="S瀏覽文件夾";
bi.ulFlags=BIF_RETURNONLYFSDIRS;
LPITEMIDLIST idl=SHBrowseForFolder(&bi);
if(idl==NULL)
return;
就能打開f:
下面的例子中返回路徑,如果沒有選,返回"",選擇了路徑,則返回選擇的路徑。
char *GetPath(HWND hWnd,char *pBuffer)
{
BROWSEINFO bf;
LPITEMIDLIST lpitem;
memset(&bf,0,sizeof BROWSEINFO);
bf.hwndOwner=hWnd;
bf.lpszTitle="選擇路徑";
bf.ulFlags=BIF_RETURNONLYFSDIRS; //屬性你可自己選擇
lpitem=SHBrowseForFolder(&bf);
if(lpitem==NULL) //如果沒有選擇路徑則返回 0
return "";
//如果選擇了路徑則復制路徑,返回路徑長度
SHGetPathFromIDList(lpitem,pBuffer);
return pBuffer;
}