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.
This topic is 9 years and 1 month old and has exceeded the time allowed for comments. Please begin a new topic or use the search feature to find a similar but newer topic.
Locked
User avatar
PGomersall
Posts: 138
Last visit: Thu Apr 11, 2024 7:51 am

A better folderbrowserdialog

Post by PGomersall »

I just read the post by cgkomodo about how to use the Windows forms folderbrowserdialog
fbd.png
fbd.png (24.65 KiB) Viewed 16519 times
and thought that I should post info on what may be a better option
fsd.png
fsd.png (26.98 KiB) Viewed 16518 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
Last visit: Thu Oct 22, 2015 1:10 pm

A better folderbrowserdialog

Post by SAPIEN Support Forums »

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
Last visit: Mon Jul 08, 2019 8:55 am
Been upvoted: 2 times

Re: A better folderbrowserdialog

Post by davidc »

[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: 138
Last visit: Thu Apr 11, 2024 7:51 am

Re: A better folderbrowserdialog

Post by PGomersall »

David - can you explain what a control set is?
Regards,
Pete
User avatar
davidc
Posts: 5913
Last visit: Mon Jul 08, 2019 8:55 am
Been upvoted: 2 times

Re: A better folderbrowserdialog

Post by davidc »

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.
This topic is 9 years and 1 month old and has exceeded the time allowed for comments. Please begin a new topic or use the search feature to find a similar but newer topic.
Locked