VC弹出选择文件夹对话框和弹出本地指定文件夹窗口
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;
}