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函數,可以通過回調函數設置標題及路徑

 

基本實現的源碼如下

 

  1. void CTestDlg::OnBtnTest()   
  2. {  
  3.     // TODO: Add your control notification handler code here  
  4.     TCHAR pszPath[MAX_PATH];  
  5.     BROWSEINFO bi;   
  6.     bi.hwndOwner      = this->GetSafeHwnd();  
  7.     bi.pidlRoot       = NULL;  
  8.     bi.pszDisplayName = NULL;   
  9.     bi.lpszTitle      = TEXT("請選擇文件夾");   
  10.     bi.ulFlags        = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;  
  11.     bi.lpfn           = NULL;   
  12.     bi.lParam         = 0;  
  13.     bi.iImage         = 0;   
  14.   
  15.     LPITEMIDLIST pidl = SHBrowseForFolder(&bi);  
  16.     if (pidl == NULL)  
  17.     {  
  18.         return;  
  19.     }  
  20.   
  21.     if (SHGetPathFromIDList(pidl, pszPath))  
  22.     {  
  23.         AfxMessageBox(pszPath);  
  24.     }  
  25. }  

 

這一般均能夠滿足要求,但有時還是需要在此基礎上增強一些功能。

比如在彈出選擇文件夾對話框時選中默認的文件夾,或在STATUSTEXT區域顯示一些信息等等。這需要在BrowseCallbackProc回調函數中實現。具體實現的源碼如下:

(具體BrowseCallBackFun回調函數的用法請參照MSDN)

 

  1. //選擇文件夾對話框回調函數   
  2. int CALLBACK BrowseCallBackFun(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)  
  3. {  
  4.     switch(uMsg)  
  5.     {  
  6.     case BFFM_INITIALIZED:  //選擇文件夾對話框初始化  
  7.         //設置默認路徑為lpData即'D:\'   
  8.         ::SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);  
  9.         //在STATUSTEXT區域顯示當前路徑   
  10.         ::SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, lpData);  
  11.         //設置選擇文件夾對話框的標題   
  12.         ::SetWindowText(hwnd, TEXT("請先設置個工作目錄"));   
  13.         break;  
  14.     case BFFM_SELCHANGED:   //選擇文件夾變更時  
  15.         {  
  16.             TCHAR pszPath[MAX_PATH];  
  17.             //獲取當前選擇路徑   
  18.             SHGetPathFromIDList((LPCITEMIDLIST)lParam, pszPath);  
  19.             //在STATUSTEXT區域顯示當前路徑  
  20.             ::SendMessage(hwnd, BFFM_SETSTATUSTEXT, TRUE, (LPARAM)pszPath);  
  21.         }  
  22.         break;  
  23.     }  
  24.     return 0;  
  25. }  
  26.   
  27.   
  28. void CTestDlg::OnBtnTest()   
  29. {  
  30.     // TODO: Add your control notification handler code here  
  31.     TCHAR pszPath[MAX_PATH];  
  32.     BROWSEINFO bi;   
  33.     bi.hwndOwner      = this->GetSafeHwnd();  
  34.     bi.pidlRoot       = NULL;  
  35.     bi.pszDisplayName = NULL;   
  36.     bi.lpszTitle      = TEXT("請選擇文件夾");   
  37.     bi.ulFlags        = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT;  
  38.     bi.lpfn           = BrowseCallBackFun;     //回調函數  
  39.     bi.lParam         = (LPARAM)TEXT("D:\\");  //傳給回調函數的參數,設置默認路徑  
  40.     bi.iImage         = 0;   
  41.       
  42.     LPITEMIDLIST pidl = SHBrowseForFolder(&bi);  
  43.     if (pidl == NULL)  
  44.     {  
  45.         return;  
  46.     }  
  47.       
  48.     if (SHGetPathFromIDList(pidl, pszPath))  
  49.     {  
  50.         AfxMessageBox(pszPath);  
  51.     }  
  52. }  

附圖片:

 

當然也可以設置選擇文件對話框的其他樣式,比如使其具有新增文件夾的功能,可如下實現

bi.ulFlags        =        BIF_USENEWUI        

附圖:(改圖來源於www.VCKBASE.com,本人電腦上安裝的是VC6,不支持BIF_USENEWUI,請在VC2003+上嘗試)

具體請參照MSDN

 

定義

WINSHELLAPI LPITEMIDLIST WINAPI SHBrowseForFolder( LPBROWSEINFO lpbi); 

結構

BROWSEINFO的結構如下:
typedef struct _browseinfo {
HWND hwndOwner; // 父 窗口句柄
LPCITEMIDLIST pidlRoot; // 要顯示的文件夾的根(Root)
LPTSTR pszDisplayName; // 保存被選取的文件夾路徑的緩沖區
LPCTSTR lpszTitle; // 顯示位於對話框左上部的標題
UINT ulFlags; // 指定對話框的外觀和功能的標志
BFFCALLBACK lpfn; // 處理事件的回調函數
LPARAM lParam; // 應用程序傳給 回調函數的參數
int iImage; // 保存被選取的文件夾的圖片索引
BROWSEINFO, *PBROWSEINFO, *LPBROWSEINFO

成員變量

hwndOwner:瀏覽文件夾對話框的父 窗體句柄。
pidlRoot:ITEMIDLIST結構的地址,包含瀏覽時的初始根目錄,而且只有被指定的目錄和其子目錄才顯示在瀏覽文件夾對話框中。該 成員變量可以是NULL,在此時桌面目錄將被使用。
pszDisplayName:用來保存用戶選中的目錄字符串的內存地址。該緩沖區的大小缺省是定義的MAX_PATH 常量宏。
lpszTitle:該瀏覽文件夾對話框對話框的顯示文本,用來提示該瀏覽文件夾對話框的功能、作用和目的。
ulFlags:該標志位描述了對話框的選項。它可以為0,也可以是以下 常量的任意組合:
BIF_BROWSEFORCOMPUTER:返回計算機名。除非用戶選中瀏覽器中的一個計算機名,否則該對話框中的“OK”按鈕為灰色。
BIF_BROWSEFORPRINTER:返回打印機名。除非選中一個打印機名,否則“OK”按鈕為灰色。
BIF_BROWSEINCLUDEFILES:瀏覽器將顯示目錄,同時也顯示文件。
BIF_DONTGOBELOWDOMAIN:在樹形視窗中,不包含域名底下的網絡目錄結構。
BIF_EDITBOX:瀏覽對話框中包含一個編輯框,在該編輯框中用戶可以輸入選中項的名字。
BIF_RETURNFSANCESTORS:返回文件系統的一個節點。僅僅當選中的是有意義的節點時,“OK”按鈕才可以使用。
BIF_RETURNONLYFSDIRS:僅僅返回文件系統的目錄。例如:在瀏覽文件夾對話框中,當選中任意一個目錄時,該“OK”按鈕可用,而當選中“ 我的電腦”或“ 網上鄰居”等非有意義的節點時,“OK”按鈕為灰色。
BIF_STATUSTEXT:在對話框中包含一個狀態區域。通過給對話框發送消息使 回調函數設置狀態文本。
BIF_VALIDATE:當沒有BIF_EDITBOX標志位時,該標志位被忽略。如果用戶在編輯框中輸入的名字非法,瀏覽對話框將發送BFFM_VALIDATEFAILED消息給回調函數。
lpfn:應用程序定義的瀏覽對話框 回調函數的地址。當對話框中的事件發生時,該對話框將調用 回調函數。該參數可用為NULL。
lParam:對話框傳遞給 回調函數的一個參數 指針
iImage:與選中目錄相關的圖像。該圖像將被指定為系統圖像列表中的索引值。

 

------------------------------------------------------

若要建立可以新建文件夾的對話框:

在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;

}  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM