Click here to Skip to main content
15,867,568 members
Articles / Desktop Programming / MFC
Article

SHBrowseForFolder with Explorer style file open dialog PlacesBar (Toolbar)

Rate me:
Please Sign up or sign in to vote.
3.88/5 (15 votes)
22 Oct 2006CPOL2 min read 68.3K   1.8K   36   7
Now, SHBrowseForFolder has the same look and feel as the Explorer file open dialog box.

Sample Image - BrowseForFolder_Toolbar.jpg

Introduction

The UI of the file open dialog has always fascinated me. I have customized it according to my needs in one of my previous articles. Its Places Bar looks very neat. Its inclusion in the file open dialog makes it easier to use. I have tried the same thing in this article: to make SHBrowseForFolder look better and make it more useful to the user. Here, I am adding the same Places Bar in this dialog box.

How it Works

First of all, here is the code which shows the dialog:

CoInitialize(NULL);
BROWSEINFO bi;
ZeroMemory(&bi, sizeof(bi));
TCHAR szDisplayName[MAX_PATH];
szDisplayName[0] = '\0';
bi.hwndOwner = NULL;
bi.pidlRoot = NULL;
bi.pszDisplayName = szDisplayName;
bi.lpszTitle = TEXT("Browsing");
bi.ulFlags = BIF_EDITBOX | BIF_VALIDATE ;
bi.lpfn = BrowseCallbackProc;
bi.lParam = NULL;
bi.iImage = 0;
SHBrowseForFolder(&bi);
CoUninitialize();

In the above code, everything is normal. I am just showing the dialog, but here, I also set the callback function for the dialog, so I can know what is happening at any time and do customizing work at the proper time.

Handling the Callback Function

I handled this function because it is easier to take advantage of this function using hooks. You can see my article on working with hooks here. In this callback function, first, I handle the BFFM_INITIALIZED message which is sent when the dialog box is complete in memory but not yet shown. It is the best time to do any modification or customization according to your needs. I also do the same here, and make the SHBrowseForFolder dialog a bit fatter so it can have the appropriate place to reside with my toolbar over it. Here is the code that creates my toolbar:

hwndTB = CreateWindowEx(WS_EX_NOPARENTNOTIFY  TOOLBARCLASSNAME, (LPSTR) NULL, 
         WS_CHILD |WS_VISIBLE|WS_TABSTOP| CCS_TOP|CCS_NODIVIDER | 
         CCS_NOPARENTALIGN |CCS_NORESIZE| TBSTYLE_FLAT|TBSTYLE_TRANSPARENT|
         TBSTYLE_WRAPABLE|TBSTYLE_TOOLTIPS|TBSTYLE_CUSTOMERASE, 
         pt.x-40,pt.y+55,84,272,hwnd, NULL, NULL, NULL);

Another message which I handle in this callback is BFFM_SELCHANGED. This is sent whenever the user changes the selection in the dialog. Here, I update my toolbar to update itself according to the selection. E.g., whenever the user selects "My Documents" from the tree control of the SHBrowseForFolder dialog, the Toolbar button which has "My Documents" written over it will be pressed.

char Path[MAX_PATH];
SHGetPathFromIDList((LPCITEMIDLIST)lParam, Path);
LPITEMIDLIST pidl ,pidl2;
pidl2 = (LPITEMIDLIST)lParam;// user selection

SHGetSpecialFolderLocation(NULL,CSIDL_DESKTOP, &pidl);// Desktop

if(ILIsEqual(pidl,pidl2))
{::SendMessage(hwndTB,TB_CHECKBUTTON,(WPARAM) ID_FIRST_BUTTON,(LPARAM)true); 
return 0;}
SHGetSpecialFolderLocation(NULL,CSIDL_PERSONAL, &pidl);//My Documents

if(ILIsEqual(pidl,pidl2) || ILIsParent(pidl,pidl2,false))
{::SendMessage(hwndTB,TB_CHECKBUTTON,(WPARAM) ID_SECOND_BUTTON,(LPARAM)true);
return 0;}
SHGetSpecialFolderLocation(NULL,CSIDL_DRIVES , &pidl);// My Computer still problems

if(ILIsEqual(pidl,pidl2) || ILIsParent(pidl,pidl2,false))
{::SendMessage(hwndTB,TB_CHECKBUTTON,(WPARAM) ID_THIRD_BUTTON,(LPARAM)true);
return 0;}
SHGetSpecialFolderLocation(NULL,CSIDL_NETWORK , &pidl);// My Network 

if(ILIsEqual(pidl,pidl2) || ILIsParent(pidl,pidl2,false))
{::SendMessage(hwndTB,TB_CHECKBUTTON,(WPARAM) ID_FOURTH_BUTTON,(LPARAM)true);
return 0;

Watching Messages for More Customizations

Actually, as you see, the file open dialog's Places Bar has a different background than the dialog itself. So, to do this and to handle the messages which my Toolbar will originate, I must watch the messages of the SHBrowseForFolder dialog. One way to do this is to use hooks, but here, I am trying another method: I just grab the default dialogProc, takes its pointer, and place my own dialogproc here. In this dialogproc, I only handle those messages which interest me, and throw every thing else to the default dialogProc.

Changing the Background

Here, I handle the NM_CUSTOMDRAW notification:

case NM_CUSTOMDRAW :
    if( pcd->nmcd.hdr.hwndFrom == hwndTB)
    {
        switch(pcd->nmcd.dwDrawStage)
        {
            case CDDS_PREPAINT :
                FillRect(pcd->nmcd.hdc, &pcd->nmcd.rc,hBkBrush);
                FrameRect(pcd->nmcd.hdc, &pcd->nmcd.rc, 
                         (HBRUSH)GetStockObject(LTGRAY_BRUSH)); 
                SelectObject(pcd->nmcd.hdc,GetStockObject(WHITE_PEN));
                MoveToEx(pcd->nmcd.hdc, pcd->nmcd.rc.right, pcd->nmcd.rc.top,NULL);
                LineTo(pcd->nmcd.hdc,pcd->nmcd.rc.left,pcd->nmcd.rc.top);
                LineTo(pcd->nmcd.hdc,pcd->nmcd.rc.left,pcd->nmcd.rc.bottom);
                SetWindowLong(hwnd , DWL_MSGRESULT, CDRF_NOTIFYITEMDRAW );
                return true;
            case CDDS_ITEMPREPAINT : 
                SetWindowLong(hwnd , DWL_MSGRESULT,CDRF_NOTIFYPOSTPAINT );
                return true;
            case CDDS_ITEMPOSTPAINT :
                int index = pcd->nmcd.lItemlParam;
                ImageList_Draw(hIL, index, pcd->nmcd.hdc, 24,11+(index*68), 
                               ILD_TRANSPARENT);
                SetWindowLong(hwnd , DWL_MSGRESULT,CDRF_NOTIFYPOSTPAINT );
                return true;
        }
    }
    break;

ToolTips

Here is the code:

case TTN_GETDISPINFO :
    LPNMTTDISPINFO lpnmtdi = (LPNMTTDISPINFO) lParam;
    switch(lpnmtdi->hdr.idFrom)
    {
        case ID_FIRST_BUTTON : 
            lpnmtdi->lpszText = MAKEINTRESOURCE(IDS_STRING_DESKTOP_TT); 
            break;
        case ID_SECOND_BUTTON :
            lpnmtdi->lpszText = MAKEINTRESOURCE(IDS_STRING_MYDOCU_TT);
            break;
        case ID_THIRD_BUTTON :
            lpnmtdi->lpszText = MAKEINTRESOURCE(IDS_STRING_MYCOMP_TT);
            break;
        case ID_FOURTH_BUTTON : 
        lpnmtdi->lpszText = MAKEINTRESOURCE(IDS_STRING_MYNET_TT); 
        break    ;
    } 
    break;

My Toolbar Changes the Selection in the SHBrowseForFolder Dialog

Yes, everything is here:

HWND htree = ::FindWindowEx(hwnd,NULL,"SysTreeView32",NULL);
HWND hedit = ::FindWindowEx(hwnd,NULL,"Edit",NULL);
switch(LOWORD(wParam))
{
    case ID_FIRST_BUTTON:
    {
        HTREEITEM hRoot,hDesktopItem;
        hRoot = TreeView_GetRoot(htree);
        TreeView_SelectSetFirstVisible(htree,hRoot, TVGN_FIRSTVISIBLE);
        TVITEM pitem;
        pitem.hItem = hRoot;
        pitem.mask = TVIF_TEXT;
        char buffer[100];
        pitem.pszText = buffer;
        pitem.cchTextMax = 99;
        TreeView_GetItem(htree,&pitem); 
        ::SendMessage(hedit,WM_SETTEXT,(WPARAM) 0,(LPARAM)pitem.pszText);
        break;

Updates

I will try to update this article more. And, my blog is an ongoing process in this direction, which I keep updating with my VC++ tips.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Pakistan Pakistan
tanvon malik ( real name Tanveer Ahmad )I am a CNC Programmer, cnc setter, cnc operator. want to know about me visit my websites.
CNC Programming
CNC Manuals
DXF & GCode Files with Online Viewers
Komment.me

I been in Switzerland MAG former +FMS+ for CNC training.


Most interesting technologies I like COM MFC DirectShow such as filter development. I am from Pakistan.
Have worked on projects mostly related video capturing, video data processing and real time object tracking on videos. For these I have worked on projects which use "Open CV Library". Which is mostly used for capturing data and its processing.

Comments and Discussions

 
GeneralPort to VC6! Pin
cristitomi19-Mar-07 3:36
cristitomi19-Mar-07 3:36 
GeneralMemory Pin
Abraxas2320-Dec-06 2:22
Abraxas2320-Dec-06 2:22 
GeneralQuestion Pin
Tibi_Kron14-Dec-06 5:44
Tibi_Kron14-Dec-06 5:44 
GeneralIt crashes Pin
Leon Salters26-Oct-06 1:38
Leon Salters26-Oct-06 1:38 
GeneralRe: It crashes Pin
tanvon malik26-Oct-06 1:51
tanvon malik26-Oct-06 1:51 
GeneralRe: It crashes [modified] Pin
srikanth_shettigar18-Jan-07 0:48
srikanth_shettigar18-Jan-07 0:48 
GeneralRe: It crashes Pin
golamp20-Jan-07 23:04
golamp20-Jan-07 23:04 
may be the version of shell32.dll is the reason !?

cause ILIsEqual() and ILIsParent()
are only in shell32.dll version 5.0 or later !!!

just an idea...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.