Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Extend OpenFileDialog and SaveFileDialog the easy way

, 22 Mar 2012
Customize OpenFileDialog and SaveFileDialog using a User Control
CustomFileDialog_old.zip
CustomFileDialog
Properties
Settings.settings
FileDlgExtenders
Properties
CustomFileDialog_src2.zip
VB_Code
FileDlgExtenders
CSharp_Code
FileDlgExtenders
Properties
Properties
My Project
Settings.settings
My Project
Settings.settings
bin
release
bin
Debug
Release
obj
Debug
DesignTimeResolveAssemblyReferencesInput.cache
Refactor
FileDlgExtenders.dll
TempPE
Release
Refactor
TempPE
CustomFileDialog_src_old.zip
c__projects_customfiledialog_src2.zip
Settings.settings
Settings.settings
DesignTimeResolveAssemblyReferencesInput.cache
FileDlgExtenders.dll
CustomFileDialog_src_old.zip
c__projects_release2.zip
FileDlgExtenders.dll
CustomOpenFileDialog.exe
release2.zip
FileDlgExtenders.dll
CustomOpenFileDialog.exe
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Security;
using System.Runtime.InteropServices;
using System.Text;

namespace FolderBrowser
{
	public delegate bool ShowNewButtonHandler(string selectedPath);

	/// Created By:				Goldberg Royee
	/// Date:					6/8/2006
	/// Reason:					This class is an extended class
	/// for the folderBrowseDialog of .NET.
	/// This class add a functionality to disable the 'Make New Folder' Button
	/// whenever a CD path selected.
	public class ExtendedFolderBrowser
	{
		#region Win32API Class
		/// <summary>
		/// 
		/// </summary>
		private class Win32API
		{
			[DllImport("shell32.dll", CharSet=CharSet.Auto)]
			public static extern IntPtr SHBrowseForFolder([In] BROWSEINFO lpbi);
			[DllImport("shell32.dll")]
			public static extern int SHGetMalloc([Out, MarshalAs(UnmanagedType.LPArray)] IMalloc[] ppMalloc);
			[DllImport("shell32.dll", CharSet=CharSet.Auto)]
			public static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath);
			[DllImport("shell32.dll")]
			public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, ref IntPtr ppidl);
			[DllImport("user32.dll", CharSet=CharSet.Auto)]
			public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, string lParam);
			[DllImport("user32.dll", EntryPoint="SendMessage", CharSet=CharSet.Auto)]
			public static extern IntPtr SendMessage2(HandleRef hWnd, int msg, int wParam, int lParam);
			[DllImport("user32.dll")] 
			public static extern bool EnableWindow(IntPtr hWnd,bool bEnable);
			[DllImport("user32.dll") ] 
			public static extern int GetWindowText(int hWnd, StringBuilder text, int count); 
			[DllImport("user32.dll") ] 
			public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
			[DllImport("user32.dll")] 
			public static extern IntPtr GetWindowThreadProcessId(IntPtr hwnd, IntPtr lpdwProcessId); 

			[DllImport("user32.dll") ] 
			public static extern IntPtr FindWindowEx(IntPtr hwndParent,
				IntPtr hwndChildAfter,
				string lpszClass,
				string lpszWindow
				);
			

			[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto), ComVisible(false)]
			public class BROWSEINFO
			{
				public IntPtr hwndOwner;
				public IntPtr pidlRoot;
				public IntPtr pszDisplayName;
				public string lpszTitle;
				public int ulFlags;
				public BrowseCallbackProc lpfn;
				public IntPtr lParam;
				public int iImage;
			}
			
			[ComImport, Guid("00000002-0000-0000-c000-000000000046"), SuppressUnmanagedCodeSecurity, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
			public interface IMalloc
			{
				IntPtr Alloc(int cb);
				void Free(IntPtr pv);
				IntPtr Realloc(IntPtr pv, int cb);
				int GetSize(IntPtr pv);
				int DidAlloc(IntPtr pv);
				void HeapMinimize();
			}
 
			public const int WM_LBUTTONDOWN = 0x0201;
			public const int BFFM_INITIALIZED = 1;
			public const int BFFM_SELCHANGED = 2;

			public delegate int BrowseCallbackProc(IntPtr hwnd, int msg, IntPtr lParam, IntPtr lpData);
		}
		#endregion

		#region InternalFolderBrowser Class
		/// <summary>
		/// 
		/// </summary>
		private class InternalFolderBrowser : CommonDialog
		{
			private string m_selectedPath = null;
			private Environment.SpecialFolder m_rootFolder;
			public event EventHandler SelectedFolderChanged;
			private string m_descriptionText = String.Empty;
			/// <summary>
			/// 
			/// </summary>
			/// <returns></returns>
			private Win32API.IMalloc GetSHMalloc()
			{
				Win32API.IMalloc[] mallocArray1 = new Win32API.IMalloc[1];
				Win32API.SHGetMalloc(mallocArray1);
				return mallocArray1[0];
			}

			/// <summary>
			/// 
			/// </summary>
			public override void Reset()
			{
				m_rootFolder = Environment.SpecialFolder.Desktop;
				m_selectedPath = string.Empty;
			}

			/// <summary>
			/// 
			/// </summary>
			/// <param name="hwndOwner"></param>
			/// <returns></returns>
			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;
			}

			/// <summary>
			/// 
			/// </summary>
			/// <param name="hwnd"></param>
			/// <param name="msg"></param>
			/// <param name="lParam"></param>
			/// <param name="lpData"></param>
			/// <returns></returns>
			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;
			}

			/// <summary>
			/// 
			/// </summary>
			public string SelectedPath
			{
				get
				{
					return m_selectedPath;
				}
			}

			/// <summary>
			/// 
			/// </summary>
			public string Description
			{
				get
				{
					return m_descriptionText;
				}
				set
				{
					m_descriptionText = (value == null) ? string.Empty : value;

				}
			}

		}
		#endregion
		
		#region Private Members

		private InternalFolderBrowser m_InternalFolderBrowser = null;
		public event EventHandler SelectedFolderChanged;
		private const string MAKE_NEW_FOLDER_BUTTON = "&Make New Folder";
		
		private ShowNewButtonHandler m_ShowNewButtonHandler = null;
		private const string BROWSE_FOR_FOLDER_CLASS_NAME =  "#32770";
		#endregion

		#region CTOR
		/// <summary>
		/// 
		/// </summary>
		public ExtendedFolderBrowser()
		{
			m_InternalFolderBrowser = new InternalFolderBrowser();
			m_InternalFolderBrowser.SelectedFolderChanged+=new EventHandler(m_InternalFolderBrowser_SelectedFolderChanged);
		}
		#endregion

		#region  Helper Methods
		/// <summary>
		/// 
		/// </summary>
		/// <param name="handle"></param>
		/// <param name="state"></param>
		private void UpdateButtonState(IntPtr handle, bool state)
		{
			if(handle != IntPtr.Zero)
			{
				Win32API.EnableWindow(handle,state);
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="windowHandle"></param>
		/// <returns></returns>
		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);
				}
			}
		}


		/// <summary>
		/// A different version for finding the 'Make New Foler' button handler.
		/// We currently don't use this version, because it requires a Localization.
		/// </summary>
		/// <returns></returns>
		private IntPtr GetButtonHandle()
		{
			//This should be using localization!!!!!!!!!!!!!!!!!#32770 (Dialog)
			IntPtr root = Win32API.FindWindow(null,"Browse For Folder");
			IntPtr child = Win32API.FindWindowEx(root,IntPtr.Zero,"Button", null);
			return child;
		}

		/// <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);
				}
			}
		}

		private void m_InternalFolderBrowser_SelectedFolderChanged(object sender, EventArgs e)
		{
			CheckState();

			//Fire event selection has changed in case anyone wants 
			//to be notified.
			if(SelectedFolderChanged != null)
			{
				SelectedFolderChanged(sender,e);
			}
		}
		#endregion

		#region FolderBrowserDialog Mathods

		public DialogResult ShowDialog()
		{
			return ShowDialog(null);
		}

		public DialogResult ShowDialog(IWin32Window owner)
		{
			return m_InternalFolderBrowser.ShowDialog(owner);
		}

		public string SelectedPath
		{
			get
			{
				return m_InternalFolderBrowser.SelectedPath;
			}
		}

		public string Description
		{
			get
			{
				return m_InternalFolderBrowser.Description;
			}
			set
			{
				m_InternalFolderBrowser.Description = value;
			}
		}

		/// <summary>
		/// Pass the delegate to your function, which will decide
		/// if to enable the 'Make New Folder' button or not.
		/// </summary>
		public ShowNewButtonHandler SetNewFolderButtonCondition
		{
			get
			{
				return m_ShowNewButtonHandler;;
			}
			set
			{
				m_ShowNewButtonHandler = value;
			}
		}

		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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

Share

About the Author

dmihailescu
Software Developer (Senior)
United States United States
Decebal Mihailescu is a software engineer with interest in .Net, C# and C++.

| Advertise | Privacy | Mobile
Web02 | 2.8.140926.1 | Last Updated 22 Mar 2012
Article Copyright 2007 by dmihailescu
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid