Click here to Skip to main content
Click here to Skip to main content

SHBrowseForFolder with Explorer style file open dialog PlacesBar (Toolbar)

, 22 Oct 2006 CPOL
Rate this:
Please Sign up or sign in to vote.
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)

Share

About the Author

tanvon malik

Pakistan Pakistan
tanvon malik ( real name Tanveer Ahmad )I am a CNC Programmer, cnc setter, cnc operator. want to know about me visit my blog.
CNC Programming Blog
 
Reach Me
cnc blog | facebook | twitter | linkedin | flickr | google+
 
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.
 
my vc++ blog tanvon++
my blog about DirectShow in my national language URDU DirectShow.wordpress.com
and below is the place where we discuss DirectShow and VC++
http://groups.yahoo.com/group/tanvon
Follow on   Twitter

Comments and Discussions

 
GeneralMemory PinmemberAbraxas2320-Dec-06 3:22 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 22 Oct 2006
Article Copyright 2006 by tanvon malik
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid