How do I open a FolderBrowserDialog at the selected folder?
Asked Answered
F

9

27

I have a FolderBrowserDialog, displayed with code shown below. However, it keeps opening with 'Computer', i.e. the root of the folder tree, selected. How do I get it to open on the selected folder?

       var folderBrowser = new FolderBrowserDialog();
        folderBrowser.Description = "Select Chase 6 Installation Folder";
        folderBrowser.RootFolder = Environment.SpecialFolder.MyComputer;
        folderBrowser.ShowNewFolderButton = false;
        if (Directory.Exists(Properties.Settings.Default.defaultChasePath))
        {
            string x = Properties.Settings.Default.defaultChasePath;
            folderBrowser.SelectedPath = x;
        }
        if (folderBrowser.ShowDialog(this) == DialogResult.OK)
        {
            chasePathtext.Text = folderBrowser.SelectedPath;
        }
Fluorescein answered 1/4, 2009 at 13:8 Comment(0)
I
21

If you set RootFolder to Environment.SpecialFolder.Desktop then it will open to the SelectedFolder as long as the path is valid.

When you set RootFolder to Environment.SpecialFolder.MyComputer, then the first time the dialog opens, it will always start at MyComputer, not the SelectedFolder path.

If a valid selection is made, then subsequent uses of the same FolderBrowserDialog instance will open at the previously selected path.

Interpreter answered 9/3, 2010 at 2:19 Comment(3)
This is not the behavior I see. My RootFolder is set to MyComputer, and I can set SelectedFolder to "C:\Almo" and it will open there. What it won't do is open at "C:\Users\Almo\Desktop" which I suspect is a permissions issue.Kraken
@Kraken Set RootFolder and SelectedFolder to Environment.SpecialFolder.Desktop. Then, presuming you are running as Almo, it should open to "C:\Users\Almo\Desktop".Interpreter
Whups, I got true and false mixed up (don't ask). Ignore my dissenting upvote on Almo's comment. +1Magen
S
10

From the Microsoft help for FolderBrowserDialog class:

Typically, after creating a new FolderBrowserDialog, you set the RootFolder to the location from which to start browsing. Optionally, you can set the SelectedPath to an absolute path of a subfolder of RootFolder that will initially be selected.

Are you possibly setting the SelectedPath to a location that doesn't equate to a subfolder of RootFolder (i.e. My Computer)? That would probably cause it to dive back to the RootFolder as the presented location.

Sanctified answered 1/4, 2009 at 13:19 Comment(1)
A 'c:\yadda yadda' path should be a sub folder of 'My Computer', shouldn't it?Fluorescein
D
10

Set rootfolder to

Environment.SpecialFolder.Desktop

and it should work as you want.

It is the only way to actually set the initial selected folder. You'd think using

Environment.SpecialFolder.MyComputer

would work, but it doesn't.

Delmardelmer answered 1/3, 2012 at 11:44 Comment(1)
It is the only way only because Microsoft didn't implement it properly.Meritocracy
U
6

This works for me:

FolderBrowserDialog diag = new FolderBrowserDialog();
diag.Description = "Select a folder in which to save your workspace...";
diag.SelectedPath = Application.StartupPath;

if (DialogResult.OK == diag.ShowDialog())
{
    // do something here...
}

Set the SelectedPath property, not RootFolder.

EDIT: Here's a screenshot showing the Application.StartupPath being in "C:\LocalDocuments\Visual Studio 2010\Projects\FolderBrowserDialogTest\FolderBrowserDialogTest\bin\Debug", which is most definitely not in the Desktop directory.

enter image description here

Unlikelihood answered 12/9, 2012 at 13:54 Comment(3)
This works only when application is placed somewhere along the subfolders of DesktopEvacuee
I edited by adding a screenshot showing that it has nothing to do with the Desktop. It works fine elsewhere.Unlikelihood
just to clarify Application is System.Windows.Forms.ApplicationSignification
E
2

The folderbrowser dialog is a wrapper around a windows API call that does not match all the relevant paths possible ( I suppose that would be an uphill task..) this can be mimicked using your own class though. Generate the following class inside your application (adding namespace and System.Reflection)

public class RootSetter
{
    [Flags()]
    public enum CsIdl
    {
        Desktop = 0x0000, // Desktop
        Internet = 0x0001, // Internet Explorer (icon on desktop)
        Programs = 0x0002, // Start Menu\Programs
        Controls = 0x0003, // My Computer\Control Panel
        Printers = 0x0004, // My Computer\Printers
        Personal = 0x0005, // My Documents
        Favorites = 0x0006, // user name\Favorites
        Startup = 0x0007, // Start Menu\Programs\Startup
        Recent = 0x0008, // user name\Recent
        SendTo = 0x0009, // user name\SendTo
        BitBucket = 0x000a, // desktop\Recycle Bin
        StartMenu = 0x000b, // user name\Start Menu
        MyDocuments = 0x000c, // logical "My Documents" desktop icon
        MyMusic = 0x000d, // "My Music" folder
        MyVideo = 0x000e, // "My Videos" folder
        DesktopDirectory = 0x0010, // user name\Desktop
        Drives = 0x0011, // My Computer
        Network = 0x0012, // Network Neighborhood (My Network Places)
        Nethood = 0x0013, // user name\nethood
        Fonts = 0x0014, // windows\fonts
        Templates = 0x0015,
        CommonStartMenu = 0x0016, // All Users\Start Menu
        CommonPrograms = 0x0017, // All Users\Start Menu\Programs
        CommonStartup = 0x0018, // All Users\Startup
        CommonDesktopDirectory = 0x0019, // All Users\Desktop
        AppData = 0x001a, // user name\Application Data
        PrintHood = 0x001b, // user name\PrintHood
        LocalAppData = 0x001c, // user name\Local Settings\Applicaiton Data (non roaming)
        AltStartup = 0x001d, // non localized startup
        CommonAltStartup = 0x001e, // non localized common startup
        CommonFavorites = 0x001f,
        InternetCache = 0x0020,
        Cookies = 0x0021,
        History = 0x0022,
        CommonAppdata = 0x0023, // All Users\Application Data
        Windows = 0x0024, // GetWindowsDirectory()
        System = 0x0025, // GetSystemDirectory()
        ProgramFiles = 0x0026, // C:\Program Files
        MyPictures = 0x0027, // C:\Program Files\My Pictures
        Profile = 0x0028, // USERPROFILE
        SystemX86 = 0x0029, // x86 system directory on RISC
        ProgramFilesX86 = 0x002a, // x86 C:\Program Files on RISC
        ProgramFilesCommon = 0x002b, // C:\Program Files\Common
        ProgramFilesCommonx86 = 0x002c, // x86 Program Files\Common on RISC
        CommonTemplates = 0x002d, // All Users\Templates
        CommonDocuments = 0x002e, // All Users\Documents
        CommonAdminTools = 0x002f, // All Users\Start Menu\Programs\Administrative Tools
        AdminTools = 0x0030, // user name\Start Menu\Programs\Administrative Tools
        Connections = 0x0031, // Network and Dial-up Connections
        CommonMusic = 0x0035, // All Users\My Music
        CommonPictures = 0x0036, // All Users\My Pictures
        CommonVideo = 0x0037, // All Users\My Video
        Resources = 0x0038, // Resource Direcotry
        ResourcesLocalized = 0x0039, // Localized Resource Direcotry
        CommonOemLinks = 0x003a, // Links to All Users OEM specific apps
        CdBurnArea = 0x003b, // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning
        ComputersNearMe = 0x003d, // Computers Near Me (computered from Workgroup membership)
        FlagCreate = 0x8000, // combine with CSIDL_ value to force folder creation in SHGetFolderPath()
        FlagDontVerify = 0x4000, // combine with CSIDL_ value to return an unverified folder path
        FlagNoAlias = 0x1000, // combine with CSIDL_ value to insure non-alias versions of the pidl
        FlagPerUserInit = 0x0800, // combine with CSIDL_ value to indicate per-user init (eg. upgrade)
        FlagMask = 0xFF00, // mask for all possible flag values
    }

    public static void SetRootFolder(System.Windows.Forms.FolderBrowserDialog fbd, CsIdl csidl)
    {
        Type t = fbd.GetType();
        FieldInfo fi = t.GetField("rootFolder", BindingFlags.Instance | BindingFlags.NonPublic);
        fi.SetValue(fbd, (System.Environment.SpecialFolder)csidl);
    }

and then inside your calling class when setting the root folder use the following syntax. It would be better to check the enum fior a close value should you find one but if you cannto then the one below is fairly generic and allows me to move thorugh my C drive easily enough.

private void button1_Click(object sender, EventArgs e)
    {
        FolderBrowserDialog folderBrowser = new FolderBrowserDialog(); 
        folderBrowser.Description = "Select Chase 6 Installation Folder"; 
        folderBrowser.RootFolder = Environment.SpecialFolder.ProgramFiles; 
        folderBrowser.ShowNewFolderButton = false; 

        if (Directory.Exists(Properties.Settings.Default.defaultChasePath)) 
        { 
            string x = Properties.Settings.Default.defaultChasePath; 

            //Use API Flag to set correct path, following tahter a catch all better to check
            //enum for full list
            RootSetter.SetRootFolder(folderBrowser, RootSetter.CsIdl.FlagDontVerify);

            folderBrowser.SelectedPath = x;

        } 
        if (folderBrowser.ShowDialog(this) == DialogResult.OK) 
        { 
            string huz = folderBrowser.SelectedPath; 
        }
    }

Hope that helps :)

Evvy answered 3/4, 2009 at 8:39 Comment(0)
D
1

My solution, here

I wrote this solution based on this solution by ParkerJay86. The solution worked on Windows 8 with several paths tested. Consider that your specified rootFolder should start with DriveLetter:\ like "C:\ProgramData"

        private void browseFolder_Click(object sender, EventArgs e)
        {
            String selectedPath;
            if (ShowFBD("C:\\", "Please Select a folder", out selectedPath))
            {
                MessageBox.Show(selectedPath);
            }
        }

        public bool ShowFBD(String rootFolder, String title, out String selectedPath)
        {
            var shellType = Type.GetTypeFromProgID("Shell.Application");
            var shell = Activator.CreateInstance(shellType);
            var result = shellType.InvokeMember("BrowseForFolder", BindingFlags.InvokeMethod, null, shell, new object[] { 0, title, 0, rootFolder });
            if (result == null)
            {
                selectedPath = "";
                return false;
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                while (result != null)
                {
                    var folderName = result.GetType().InvokeMember("Title", BindingFlags.GetProperty, null, result, null).ToString();
                    sb.Insert(0, String.Format("{0}\\", folderName));
                    result = result.GetType().InvokeMember("ParentFolder", BindingFlags.GetProperty, null, result, null);
                }
                selectedPath = sb.ToString();

                selectedPath = Regex.Replace(selectedPath, @"Desktop\\Computer\\.*\(\w:\)\\", rootFolder.Substring(0, 3));
                return true;
            }
        }
Disservice answered 18/10, 2012 at 9:53 Comment(0)
M
0

I was seeing this problem using:

RootFolder = Environment.SpecialFolder.MyComputer
SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

By playing around with hard-wired versions of that path's subpaths and sibling branches, I decided this really is a permissions thing. It doesn't work with C:\Users[UserID]\Documents, but it works fine with C:\Users, or C:\Users\Public\Documents (even though that doesn't exist or, at least it's called Public Documents instead of Documents), or C:\Users\Default\Documents (even though that doesn't exist or, at least it's called My Documents instead of Documents).

What I ended up doing was checking whether the SelectedPath contains "\users[UserID]", then using Environment.SpecialFolder.Desktop if it did and Environment.SpecialFolder.MyComputer if it didn't.

Probably, checking the SelectedPath folder permissions would be a better test, but this will do for the expected use cases.

Melidamelilot answered 15/9, 2017 at 15:51 Comment(0)
M
0

Microsoft's FolderBrowseDialog implementation in .Net Windows Forms is, like any other Microsoft's implementation of Windows shell or common controls, partially implemented and the part which is implemented is partially broken.

Not only it doesn't support half of the functionality of the native shell dialog, but it also doesn't initialize the control properly in case RootFolder is set to anything other than Environment.SpecialFolder.Desktop.

Here is the solution I wrote using marshaling which should work properly when starting from My Computer (which is probably what most people landing on this question wanted in the first place):

//
// MIT NO-AI License
//
// Copyright © 2023 by Igor Levicki
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// Permission is not granted to use this software or any of the associated files
// as sample data for the purposes of building machine learning models.
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

namespace NativeMethods
{
    using System;
    using System.Runtime.InteropServices;

    internal static class SHELL32
    {
        private delegate int BrowseCallBackProc(IntPtr hWnd, int msg, IntPtr lParam, IntPtr wParam);

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
        private struct BROWSEINFO
        {
            public IntPtr           Owner;
            public IntPtr           Root;
            public string           SelectedPath;
            public string           Description;
            public UInt32           Flags;
            public BrowseCallBackProc   Callback;
            public IntPtr           lParam;
            public Int32            Image;
        }

        private const Int32 MAX_PATH            = 260;
        private const Int32 BFFM_INITIALIZED        = 1;
        private const Int32 BFFM_SETEXPANDED        = 0x046A;
        private const string FOLDERID_ComputerFolder    = "{0AC0837C-BBF8-452A-850D-79D08E667CA7}";

        [Flags]
        private enum BIF : UInt32
        {
            RETURNONLYFSDIRS    = 0x00000001,
            DONTGOBELOWDOMAIN   = 0x00000002,
            STATUSTEXT      = 0x00000004,
            RETURNFSANCESTORS   = 0x00000008,
            EDITBOX         = 0x00000010,
            VALIDATE        = 0x00000020,
            NEWDIALOGSTYLE      = 0x00000040,
            USENEWUI        = NEWDIALOGSTYLE | EDITBOX,
            BROWSEINCLUDEURLS   = 0x00000080,
            UAHINT          = 0x00000100,
            NONEWFOLDERBUTTON   = 0x00000200,
            NOTRANSLATETARGETS  = 0x00000400,
            BROWSEFORCOMPUTER   = 0x00001000,
            BROWSEFORPRINTER    = 0x00002000,
            BROWSEINCLUDEFILES  = 0x00004000,
            SHAREABLE       = 0x00008000,
            BROWSEFILEJUNCTIONS = 0x00010000
        }

        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        private static extern IntPtr SHBrowseForFolder(ref BROWSEINFO lpbi);

        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        private static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath);

        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        private static extern Int32 SHILCreateFromPath(string pszPath, ref IntPtr pidl, IntPtr rgfInOut);

        [DllImport("shell32.dll", CharSet = CharSet.Unicode)]
        private static extern Int32 SHGetKnownFolderIDList(Guid rfid, UInt32 Flags, IntPtr Token, ref IntPtr pidl);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

        private static int OnBrowseEvent(IntPtr hWnd, int msg, IntPtr lParam, IntPtr lpData)
        {
            switch (msg) {
            case BFFM_INITIALIZED:
                SendMessage(hWnd, BFFM_SETEXPANDED, IntPtr.Zero, lpData);
                break;
            }

            return 0;
        }

        private static IntPtr ThisPC()
        {
            IntPtr pidl = IntPtr.Zero;

            Int32 hr = SHGetKnownFolderIDList(new Guid(FOLDERID_ComputerFolder), 0, IntPtr.Zero, ref pidl);

            return pidl;
        }

        private static IntPtr PIDLFromPath(string Path)
        {
            IntPtr pidl = IntPtr.Zero;

            Int32 hr = SHILCreateFromPath(Path, ref pidl, IntPtr.Zero);

            return pidl;
        }

        public static string SelectFolder(string Description, string InitialPath, IntPtr Owner)
        {
            BROWSEINFO bi = new BROWSEINFO();

            bi.Owner = Owner;
            bi.Description = Description;
            bi.Root = ThisPC();
            bi.Flags = (UInt32)(BIF.NONEWFOLDERBUTTON | BIF.USENEWUI);
            bi.Callback = OnBrowseEvent;
            bi.lParam = PIDLFromPath(InitialPath);

            IntPtr Buffer = Marshal.AllocHGlobal(MAX_PATH * 2);
            IntPtr pidl = IntPtr.Zero;

            try {
                pidl = SHBrowseForFolder(ref bi);

                if (pidl != IntPtr.Zero && SHGetPathFromIDList(pidl, Buffer)) {
                    return Marshal.PtrToStringUni(Buffer);
                } else {
                    return null;
                }
            } finally {
                if (pidl != IntPtr.Zero) {
                    Marshal.FreeCoTaskMem(pidl);
                }
                if (bi.lParam != IntPtr.Zero) {
                    Marshal.FreeCoTaskMem(bi.lParam);
                }
                if (bi.Root != IntPtr.Zero) {
                    Marshal.FreeCoTaskMem(bi.Root);
                }
            }
        }
    }
}

Usage example:

// InitialPath will be selected and expanded if it exists and is accessible to the user
string InitialPath = @"D:\Downloads";
// Handle is hwnd of your owner form
string SelectedPath = SHELL32.SelectFolder("Select foobar folder", InitialPath, Handle);
// SelectedPath will contain path or null on error / cancel

Full error checking as well as support for starting from known folders other than My Computer are left as an exercise for the reader.

Note that your program's Main() has to be marked with [STAThread] or the dialog will not work.

For more details about COM initialization and allowed flag combinations see SHBrowseForFolderW API documentation.

Meritocracy answered 23/12, 2023 at 20:49 Comment(0)
S
-1

To select existing path (no special f...ng folders) you must write own treeview based form.

Sr answered 23/10, 2010 at 17:31 Comment(1)
IMHO, can be better answer: stackoverflow.com/help/how-to-answer. Maybe provide context with links, code, ...Sharika

© 2022 - 2024 — McMap. All rights reserved.