5,699,431 members and growing! (17,782 online)
Email Password   helpLost your password?
Desktop Development » Dialogs and Windows » General     Intermediate

Extended Folder Browser

By Goldberg Royee

Adding functionality to a Folder Browser Dialog control.
C#, .NET, WinXP, Windows, Visual Studio, Dev

Posted: 14 Jun 2006
Updated: 20 Jun 2006
Views: 41,619
Bookmarked: 49 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
16 votes for this Article.
Popularity: 5.46 Rating: 4.53 out of 5
0 votes, 0.0%
1
0 votes, 0.0%
2
1 vote, 6.7%
3
5 votes, 33.3%
4
9 votes, 60.0%
5

Folder Browser with disabled 'Make New Folder' button

Introduction

Have you ever tried to change anything in the folder browser dialog of C#?

In this article, I will show you how you can make changes to the dialog. For example, what if we want to disable the ‘Make New Folder’ button every time a CD drive is selected? Or what if we want the user to select only from existing folders, except one folder that is to be allowed to make child folders?

Background

.NET has a FolderBrowserDialog control which is, basically, a wrapper for the Win32 function SHBrowseForFolder. The only issue here is that this class is a sealed class, so we can’t derive from it and add functionality. This control doesn’t provide any event we can register to and execute our code there. So basically, if you are satisfied with the control as it is, then you are OK. But once you need to do a small change, you have to figure out how the control really works.

In this article, we will see, step by step, how we can disable the ‘Make New Folder’ every time a CD drive is selected.

To achieve our goal, we will have to overcome two main issues:

  1. Getting the control to fire an event every time a selection is changed.
  2. Disabling the button.

Using the code

The first thing we are going to do is create an ExtendedFolderBrowser class. This class will hold a private class, InternalFolderBrowser, which will be derived from CommonDialog. InternalFolderBrowser is, basically, an implementation of the folder browser dialog, but this implementation will allow us to extend the functionality and add our new add-ins in the parent class. When deriving from the CommonDialog class, we need to implement two methods: Reset and Rundialog.

private class InternalFolderBrowser : CommonDialog
{
    private string m_selectedPath = null;
    private Environment.SpecialFolder m_rootFolder;
    public event EventHandler SelectedFolderChanged;
    private string m_descriptionText = String.Empty;
    
    public override void Reset()
    {
        m_rootFolder = Environment.SpecialFolder.Desktop;
        m_selectedPath = string.Empty;
    }

    protected override bool RunDialog(System.IntPtr hwndOwner)
    {
        IntPtr ptr1 = IntPtr.Zero;
        bool flag1 = false;
        Win32API.SHGetSpecialFolderLocation(hwndOwner, 
                         (int)m_rootFolder, ref ptr1);
        if (ptr1 == IntPtr.Zero)
        {
            Win32API.SHGetSpecialFolderLocation(hwndOwner, 0, ref ptr1);
            if (ptr1 == IntPtr.Zero)
            {
                throw new Exception("FolderBrowserDialogNoRootFolder");
            }
        }

        //Initialize the OLE to current thread.

        Application.OleRequired();
        IntPtr ptr2 = IntPtr.Zero;
        try
        {
            Win32API.BROWSEINFO browseinfo1 = new Win32API.BROWSEINFO();
            IntPtr ptr3 = Marshal.AllocHGlobal((int) 
                          (260 * Marshal.SystemDefaultCharSize));
            IntPtr ptr4 = Marshal.AllocHGlobal((int) 
                          (260 * Marshal.SystemDefaultCharSize));
            Win32API.BrowseCallbackProc proc1 = 
              new Win32API.BrowseCallbackProc(
              this.FolderBrowserDialog_BrowseCallbackProc);
            browseinfo1.pidlRoot = ptr1;
            browseinfo1.hwndOwner = hwndOwner;
            browseinfo1.pszDisplayName = ptr3;
            browseinfo1.lpszTitle = m_descriptionText;
            browseinfo1.ulFlags = 0x40;
            browseinfo1.lpfn = proc1;
            browseinfo1.lParam = IntPtr.Zero;
            browseinfo1.iImage = 0;
            ptr2 = Win32API.SHBrowseForFolder(browseinfo1);

            string s = Marshal.PtrToStringAuto(ptr3);

            if (ptr2 != IntPtr.Zero)
            {
                Win32API.SHGetPathFromIDList(ptr2, ptr4);
                this.m_selectedPath = Marshal.PtrToStringAuto(ptr4);
                Marshal.FreeHGlobal(ptr4);
                Marshal.FreeHGlobal(ptr3);
                flag1 = true;
            }
        }
        finally
        {
            Win32API.IMalloc malloc1 = GetSHMalloc();
            malloc1.Free(ptr1);
            if (ptr2 != IntPtr.Zero)
            {
                malloc1.Free(ptr2);
            }
        }
        return flag1;
    }

When we create the BROWSEINFO struct, we pass a delegate to the function that will be called on every change in the control:

private int FolderBrowserDialog_BrowseCallbackProc(IntPtr hwnd, 
                         int msg, IntPtr lParam, IntPtr lpData)
{
    switch (msg)
    {
        case Win32API.BFFM_INITIALIZED:
            if (m_selectedPath != string.Empty)
            {
                Win32API.SendMessage(new HandleRef(null, hwnd), 
                                     0x467, 1, m_selectedPath);
            }
            break;

        case Win32API.BFFM_SELCHANGED: //Selction Changed

        {
            IntPtr ptr1 = lParam;
            if (ptr1 != IntPtr.Zero)
            {
                IntPtr ptr2 = Marshal.AllocHGlobal((int) 
                              (260 * Marshal.SystemDefaultCharSize));
                bool flag1 = Win32API.SHGetPathFromIDList(ptr1, ptr2);
                m_selectedPath = Marshal.PtrToStringAuto(ptr2);

                //Fire Event

                if(SelectedFolderChanged != null)
                {
                    SelectedFolderChanged(this,null);
                }
                Marshal.FreeHGlobal(ptr2);
                Win32API.SendMessage2(new HandleRef(null, hwnd), 
                                      0x465, 0, flag1 ? 1 : 0);
            }
            break;
        }
    }
    return 0;
}

Every time a selection is changed, we will be notified by the BFFM_SELCHANGED message. Once we receive a message, we will fire an event so our parent class, ExtendedFolderBrowser, will be notified too.

The last thing we should do is register the event of the inner class and disable the button.

When the selection changes, we will execute a CheckState method:

/// <summary>

/// Check if we should disable the 'Make New Folder' button

/// </summary>

private void CheckState()
{
    if(m_ShowNewButtonHandler != null)
    {
        if(m_ShowNewButtonHandler(SelectedPath))
        {
            //Disabel the button

            UpdateButtonState(GetButtonHandle(IntPtr.Zero), false);
        }
        else
        {
            //Enable the button

            UpdateButtonState(GetButtonHandle(IntPtr.Zero),true);
        }
    }
}

m_ShowNewButtonHandler is a delegate to the user function that will check if we should disable the button according to the current selection. When a client creates a dialog, it will pass a delegate to its function to make a decision about the state of the button.

To change the state of the button, we simply use the EnableWindow API:

private void UpdateButtonState(IntPtr handle, bool state)
{
    if(handle != IntPtr.Zero)
    {
        Win32API.EnableWindow(handle,state);
    }
}

The last tricky thing left to do is to get the handle for the button. To get the handle, we will search for the class name "#32770", this is the class of the folder browser dialog. The problem is that this class name is not unique, so we have to make another check whether this handle we found is actually from the same thread that we are running. If it is from the same thread, bingo!, we have found the handler for the dialog control, otherwise we will continue searching.

Once we have found the dialog handle, all that is left is to return the first child handle of the Button type. This will be the handle to the 'Make New Folder" button.

private bool isFromTheSameThread(IntPtr windowHandle)
{
    //Get the thread that running given handler

    IntPtr activeThreadID = Win32API.GetWindowThreadProcessId(windowHandle, 
                                                              IntPtr.Zero); 

    //Get current thread

    int currentThread = AppDomain.GetCurrentThreadId();
    
    return (currentThread == activeThreadID.ToInt32());

}
        
private IntPtr GetButtonHandle(IntPtr handle)
{
    //First time

    if(handle == IntPtr.Zero)
    {
        //Get Handle for class with name "#32770"

        IntPtr parent = Win32API.FindWindow(BROWSE_FOR_FOLDER_CLASS_NAME,null);

        //If the window we found is in the same thread we are running

        //then it is The 'Browse For Folder' Dialog, otherwise keep searching

        if(!isFromTheSameThread(parent))
        {
            //Keep searching from this point

            return GetButtonHandle(parent);
        }
        else
        {
            return   Win32API.FindWindowEx(parent,IntPtr.Zero,"Button", null);
        }
    }
    else
    {
        //Find next window

        IntPtr parent = Win32API.FindWindowEx(IntPtr.Zero, 
                        handle,BROWSE_FOR_FOLDER_CLASS_NAME, null);
        if(!isFromTheSameThread(parent))
        {
            return GetButtonHandle(parent);
        }
        else
        {
            //We found the 'Browse For Folder' Dialog handler.

            //Lets return the child handler of 'Maker new Folder' button

            return   Win32API.FindWindowEx(parent,IntPtr.Zero,"Button", null);
        }
    }
}

The last thing I want to show you is how to use this control:

ExtendedFolderBrowser m_ExtendedFolderBrowser = 
                      new ExtendedFolderBrowser();

m_ExtendedFolderBrowser.Description = "Folder Browser";

//Create a hanlder to a function which will check 

//if to show the 'Make New Button' button

ShowNewButtonHandler handler = new ShowNewButtonHandler(IsCDDrive);
//Set the handler

m_ExtendedFolderBrowser.SetNewFolderButtonCondition = handler;

m_ExtendedFolderBrowser.ShowDialog();

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Goldberg Royee


I am a software engineer team leader in a company called Verint.

Our main focus is C# WinForm aplications, Remoting, Reflection, AOP and GDI+.


Occupation: Web Developer
Location: Israel Israel

Other popular Dialogs and Windows articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 16 of 16 (Total in Forum: 16) (Refresh)FirstPrevNext
GeneralMulti folder selectionmemberopans5:20 31 Mar '08  
GeneralRe: Multi folder selectionmemberalhambra-eidos2:32 29 Sep '08  
Generalasp.net 2.0memberguesswho0011:51 8 Oct '07  
GeneralDisable Acept Buttonmembereldjzar2:38 25 May '07  
GeneralRe: Disable Acept ButtonmemberGoldberg Royee10:21 5 Jun '07  
QuestionHow to extend it?memberSeikilos11:05 11 Jan '07  
AnswerRe: How to extend it?memberGoldberg Royee10:21 5 Jun '07  
GeneralRe: How to extend it?memberjmcc2k0:18 14 Nov '08  
GeneralThat's what I was looking formembersirlantis10:53 19 Jun '06  
GeneralRe: That's what I was looking formemberGoldberg Royee20:18 19 Jun '06  
GeneralGenghismemberRamon Smits22:47 18 Jun '06  
GeneralRe: GenghismemberGoldberg Royee4:35 19 Jun '06  
QuestionRe: Genghismembert4urean3:06 24 Nov '06  
AnswerRe: GenghismemberRamon Smits4:11 24 Nov '06  
GeneralExcellent [modified]memberZooZee2:08 15 Jun '06  
GeneralRe: ExcellentmemberGoldberg Royee10:48 17 Jun '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 20 Jun 2006
Editor: Smitha Vijayan
Copyright 2006 by Goldberg Royee
Everything else Copyright © CodeProject, 1999-2008
Web10 | Advertise on the Code Project