A better folderbrowserdialog

Ask questions about creating Graphical User Interfaces (GUI) in PowerShell and using WinForms controls.
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Locked
User avatar
PGomersall
Posts: 90
Joined: Sun Mar 03, 2013 12:40 pm

A better folderbrowserdialog

Post by PGomersall » Wed Mar 18, 2015 10:08 am

I just read the post by cgkomodo about how to use the Windows forms folderbrowserdialog
fbd.png
fbd.png (24.65 KiB) Viewed 9873 times
and thought that I should post info on what may be a better option
fsd.png
fsd.png (26.98 KiB) Viewed 9872 times
as it may be helpful to many; it is a really useful piece of code for me. The builtin folderbrowserdialog has several limitations and even Microsoft doesn't use it for their own applications. For me it cannot handle UNC paths and you cannot paste in a folder path to start browsing. This led me to look online for a better method to use in PowerShell UI scripts and within PowerShell Studio. What I found was a piece of C# code that extended the OpenFileDialog so that it could be used for folder browsing. This can be viewed at http://www.lyquidity.com/devblog/?p=136 This is an excellent little C# project from which I modified the code to be useful in PowerShell. Basically I use some of the source code to build a function that contains a type definition for a folderselectdialog that I then load, in the case of a PowerShell Studio GUI as part of the load event. The dialog can then be called via the new-object cmdlet.
The code is below:
PowerShell Code
Double-click the code block to select all.
Function BuildDialog {
	$sourcecode = @"
using System;
using System.Windows.Forms;
using System.Reflection;
namespace FolderSelect
{
	public class FolderSelectDialog
	{
		System.Windows.Forms.OpenFileDialog ofd = null;
		public FolderSelectDialog()
		{
			ofd = new System.Windows.Forms.OpenFileDialog();
			ofd.Filter = "Folders|\n";
			ofd.AddExtension = false;
			ofd.CheckFileExists = false;
			ofd.DereferenceLinks = true;
			ofd.Multiselect = false;
		}
		public string InitialDirectory
		{
			get { return ofd.InitialDirectory; }
			set { ofd.InitialDirectory = value == null || value.Length == 0 ? Environment.CurrentDirectory : value; }
		}
		public string Title
		{
			get { return ofd.Title; }
			set { ofd.Title = value == null ? "Select a folder" : value; }
		}
		public string FileName
		{
			get { return ofd.FileName; }
		}
		public bool ShowDialog()
		{
			return ShowDialog(IntPtr.Zero);
		}
		public bool ShowDialog(IntPtr hWndOwner)
		{
			bool flag = false;

			if (Environment.OSVersion.Version.Major >= 6)
			{
				var r = new Reflector("System.Windows.Forms");
				uint num = 0;
				Type typeIFileDialog = r.GetType("FileDialogNative.IFileDialog");
				object dialog = r.Call(ofd, "CreateVistaDialog");
				r.Call(ofd, "OnBeforeVistaDialog", dialog);
				uint options = (uint)r.CallAs(typeof(System.Windows.Forms.FileDialog), ofd, "GetOptions");
				options |= (uint)r.GetEnum("FileDialogNative.FOS", "FOS_PICKFOLDERS");
				r.CallAs(typeIFileDialog, dialog, "SetOptions", options);
				object pfde = r.New("FileDialog.VistaDialogEvents", ofd);
				object[] parameters = new object[] { pfde, num };
				r.CallAs2(typeIFileDialog, dialog, "Advise", parameters);
				num = (uint)parameters[1];
				try
				{
					int num2 = (int)r.CallAs(typeIFileDialog, dialog, "Show", hWndOwner);
					flag = 0 == num2;
				}
				finally
				{
					r.CallAs(typeIFileDialog, dialog, "Unadvise", num);
					GC.KeepAlive(pfde);
				}
			}
			else
			{
				var fbd = new FolderBrowserDialog();
				fbd.Description = this.Title;
				fbd.SelectedPath = this.InitialDirectory;
				fbd.ShowNewFolderButton = false;
				if (fbd.ShowDialog(new WindowWrapper(hWndOwner)) != DialogResult.OK) return false;
				ofd.FileName = fbd.SelectedPath;
				flag = true;
			}
			return flag;
		}
	}
	public class WindowWrapper : System.Windows.Forms.IWin32Window
	{
		public WindowWrapper(IntPtr handle)
		{
			_hwnd = handle;
		}
		public IntPtr Handle
		{
			get { return _hwnd; }
		}

		private IntPtr _hwnd;
	}
	public class Reflector
	{
		string m_ns;
		Assembly m_asmb;
		public Reflector(string ns)
			: this(ns, ns)
		{ }
		public Reflector(string an, string ns)
		{
			m_ns = ns;
			m_asmb = null;
			foreach (AssemblyName aN in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
			{
				if (aN.FullName.StartsWith(an))
				{
					m_asmb = Assembly.Load(aN);
					break;
				}
			}
		}
		public Type GetType(string typeName)
		{
			Type type = null;
			string[] names = typeName.Split('.');

			if (names.Length > 0)
				type = m_asmb.GetType(m_ns + "." + names[0]);

			for (int i = 1; i < names.Length; ++i) {
				type = type.GetNestedType(names, BindingFlags.NonPublic);
			}
			return type;
		}
		public object New(string name, params object[] parameters)
		{
			Type type = GetType(name);
			ConstructorInfo[] ctorInfos = type.GetConstructors();
			foreach (ConstructorInfo ci in ctorInfos) {
				try {
					return ci.Invoke(parameters);
				} catch { }
			}

			return null;
		}
		public object Call(object obj, string func, params object[] parameters)
		{
			return Call2(obj, func, parameters);
		}
		public object Call2(object obj, string func, object[] parameters)
		{
			return CallAs2(obj.GetType(), obj, func, parameters);
		}
		public object CallAs(Type type, object obj, string func, params object[] parameters)
		{
			return CallAs2(type, obj, func, parameters);
		}
		public object CallAs2(Type type, object obj, string func, object[] parameters) {
			MethodInfo methInfo = type.GetMethod(func, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			return methInfo.Invoke(obj, parameters);
		}
		public object Get(object obj, string prop)
		{
			return GetAs(obj.GetType(), obj, prop);
		}
		public object GetAs(Type type, object obj, string prop) {
			PropertyInfo propInfo = type.GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			return propInfo.GetValue(obj, null);
		}
		public object GetEnum(string typeName, string name) {
			Type type = GetType(typeName);
			FieldInfo fieldInfo = type.GetField(name);
			return fieldInfo.GetValue(null);
		}
	}
}
"@
	$assemblies = ('System.Windows.Forms', 'System.Reflection')
	Add-Type -TypeDefinition $sourceCode -ReferencedAssemblies $assemblies -ErrorAction STOP
}

Placing the function name "BuildDialog" in the load event places it in memory and then you can call it with something like:
PowerShell Code
Double-click the code block to select all.
$fsd = New-Object FolderSelect.FolderSelectDialog
    $fsd.Title = "What to select";
    $fsd.ShowDialog() | Out-Null

Getting the selected path back is as simple as:
PowerShell Code
Double-click the code block to select all.
$SourceFolder = $fsd.FileName

I hope people find it useful.
Pete
Last edited by PGomersall on Wed Mar 18, 2015 10:37 am, edited 7 times in total.

User avatar
SAPIEN Support Forums
Posts: 945
Joined: Wed Dec 03, 2014 2:26 pm

A better folderbrowserdialog

Post by SAPIEN Support Forums » Wed Mar 18, 2015 10:08 am

This is an automated post. A real person will respond soon.

Thank you for posting, pgomersall.

Did you remember to include the following?
  • 1. Product, version and build (e.g. Product: PowerShell Studio 2014, Version & Build: 4.1.71. Version and build information can be found in the product's About box accessed by clicking the blue icon with the 'i' in the upper right hand corner of the ribbon.)
    2. Specify if you are running a 32 or 64 bit version
    3. Specify your operating system and if it is 32 or 64 bit.
    4. Attach a screenshot if your issue can be seen on the screen
    5. Attach a zip file if you have multiple files (crash reports, log entries, etc.) related to your issue.
If not, please take a moment to edit your original post or reply to this one.

*** Make sure you do not post any licensing information ***

User avatar
davidc
Posts: 5913
Joined: Thu Aug 18, 2011 4:56 am

Re: A better folderbrowserdialog

Post by davidc » Wed Mar 18, 2015 10:48 am

[This post was moved to the PowerShell GUIs forum by the moderator]

Thank you for the posting the suggestion. Maybe I will create a Control Set for it.

David
David
SAPIEN Technologies, Inc.

User avatar
PGomersall
Posts: 90
Joined: Sun Mar 03, 2013 12:40 pm

Re: A better folderbrowserdialog

Post by PGomersall » Wed Mar 18, 2015 11:21 am

David - can you explain what a control set is?
Regards,
Pete

User avatar
davidc
Posts: 5913
Joined: Thu Aug 18, 2011 4:56 am

Re: A better folderbrowserdialog

Post by davidc » Wed Mar 18, 2015 2:04 pm

Control sets are pre-wired controls in PowerShell Studio:

http://www.sapien.com/blog/2012/04/26/powershell-studio-2012-whats-new-part-3/

David
David
SAPIEN Technologies, Inc.

Locked