|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionHave 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 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:
Using the codeThe first thing we are going to do is create an 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 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 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 /// <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);
}
}
}
To change the state of the button, we simply use the 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 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();
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||