Question

Dans VS .NET, lorsque vous sélectionnez un dossier pour un projet, une boîte de dialogue qui ressemble à OpenFileDialog ou SaveFileDialog s'affiche, mais est configurée pour accepter uniquement les dossiers.Depuis que j'ai vu ça, je voulais savoir comment c'était fait.Je connais FolderBrowserDialog, mais je n’ai jamais vraiment aimé cette boîte de dialogue.Cela commence trop petit et ne me permet pas de profiter de la possibilité de taper un chemin.

Je suis presque certain qu'il n'existe désormais aucun moyen de le faire à partir de .NET, mais je suis tout aussi curieux de savoir comment le faire à partir de code non géré.À moins de réimplémenter complètement la boîte de dialogue à partir de zéro, comment modifier la boîte de dialogue pour avoir ce comportement ?

Je voudrais également réaffirmer que je connais FolderBrowserDialog mais que parfois je n'aime pas l'utiliser, en plus d'être vraiment curieux de savoir comment configurer une boîte de dialogue de cette manière.Me dire d'utiliser simplement FolderBrowserDialog m'aide à maintenir une expérience d'interface utilisateur cohérente, mais ne satisfait pas ma curiosité, cela ne compte donc pas comme une réponse.

Ce n'est pas non plus spécifique à Vista ;Je vois cette boîte de dialogue depuis VS .NET 2003, elle est donc réalisable sous Win2k et WinXP.Il s'agit moins d'une question "Je veux savoir comment procéder correctement" que d'une question "Je suis curieux à ce sujet depuis que j'ai voulu le faire pour la première fois dans VS 2003".Je comprends que la boîte de dialogue de fichiers de Vista a une option pour ce faire, mais cela fonctionne sous XP donc je sais qu'ils l'ont fait quelque chose pour le faire fonctionner.Les réponses spécifiques à Vista ne sont pas des réponses, car Vista n'existe pas dans le contexte de la question.

Mise à jour:J'accepte la réponse de Scott Wisniewski car elle est accompagnée d'un échantillon fonctionnel, mais je pense que Serge mérite le mérite d'avoir souligné la personnalisation des boîtes de dialogue (qui est certes désagréable de .NET mais elle fait travail) et Mark Ransom pour avoir découvert que MS avait probablement lancé une boîte de dialogue personnalisée pour cette tâche.

Était-ce utile?

La solution

J'ai une boîte de dialogue que j'ai écrite appelée boîte de dialogue OpenFileOrFolder qui vous permet d'ouvrir un dossier ou un fichier.

Si vous définissez sa valeur AcceptFiles sur false, il fonctionne uniquement en mode dossier d'acceptation.

Vous pouvez télécharger la source depuis GitHub ici

Autres conseils

Il existe le pack de codes API Windows.Il contient beaucoup de choses liées au shell, y compris le CommonOpenFileDialog classe (dans le Microsoft.WindowsAPICodePack.Dialogs espace de noms).C'est la solution parfaite : la boîte de dialogue ouverte habituelle avec uniquement les dossiers affichés.

Voici un exemple de comment l'utiliser :

CommonOpenFileDialog cofd = new CommonOpenFileDialog();
cofd.IsFolderPicker = true;
cofd.ShowDialog();

Malheureusement, Microsoft ne propose plus ce package, mais plusieurs personnes ont téléchargé officieusement des binaires sur NuGet.Un exemple peut être trouvé ici.Ce package ne contient que des éléments spécifiques au shell.Si vous en avez besoin, le même utilisateur dispose de plusieurs autres packages qui offrent plus de fonctionnalités présentes dans le package d'origine.

Vous pouvez utiliser DossierBrowserDialogEx - Une dérivée réutilisable du FolderbrowserDialog intégré.Celui-ci permet de saisir un chemin, même un chemin UNC.Vous pouvez également rechercher des ordinateurs ou des imprimantes avec.Fonctionne exactement comme le FBD intégré, mais...mieux.

(MODIFIER:J'aurais dû souligner que cette boîte de dialogue peut être configurée pour sélectionner des fichiers ou des dossiers.)

Code source complet (un court module C#).Gratuit.Licence MS-Public.

Code pour l'utiliser :

var dlg1 = new Ionic.Utils.FolderBrowserDialogEx();
dlg1.Description = "Select a folder to extract to:";
dlg1.ShowNewFolderButton = true;
dlg1.ShowEditBox = true;
//dlg1.NewStyle = false;
dlg1.SelectedPath = txtExtractDirectory.Text;
dlg1.ShowFullPathInEditBox = true;
dlg1.RootFolder = System.Environment.SpecialFolder.MyComputer;

// Show the FolderBrowserDialog.
DialogResult result = dlg1.ShowDialog();
if (result == DialogResult.OK)
{
    txtExtractDirectory.Text = dlg1.SelectedPath;
}

Le Ookii.Dialogues Le package contient un wrapper géré autour de la nouvelle boîte de dialogue du navigateur de dossiers (de style Vista).Il se dégrade également gracieusement sur les anciens systèmes d'exploitation.

Mieux vaut utiliser FolderBrowserDialog pour cela.

using (FolderBrowserDialog dlg = new FolderBrowserDialog())
{
    dlg.Description = "Select a folder";
    if (dlg.ShowDialog() == DialogResult.OK)
    {
        MessageBox.Show("You selected: " + dlg.SelectedPath);
    }
}

Après des heures de recherche, j'ai trouvé cette réponse par leetNuitOmbre à une solution de travail.

Je pense qu’il y a trois choses qui rendent cette solution bien meilleure que toutes les autres.

  1. C'est simple à utiliser. Cela nécessite uniquement d'inclure deux fichiers (qui peuvent de toute façon être combinés en un seul) dans votre projet.
  2. On revient à la norme Boîte de dialogue du navigateur de dossiers lorsqu'il est utilisé sur XP ou des systèmes plus anciens.
  3. L'auteur accorde la permission d'utiliser le code à toute fin que vous jugez appropriée.

    Il n’y a pas de licence en tant que telle, vous êtes libre de prendre et de faire ce que vous voulez avec le code.

Téléchargez le code ici.

Copie audio exacte fonctionne de cette façon sur Windows XP.La boîte de dialogue standard d'ouverture de fichier s'affiche, mais le champ du nom de fichier contient le texte « Le nom de fichier sera ignoré ».

Je suppose juste ici, mais je soupçonne que la chaîne est injectée dans le contrôle d'édition de la zone de liste déroulante chaque fois qu'une modification significative est apportée à la boîte de dialogue.Tant que le champ n'est pas vide et que les indicateurs de la boîte de dialogue sont définis pour ne pas vérifier l'existence du fichier, la boîte de dialogue peut être fermée normalement.

Modifier: c'est beaucoup plus facile que je ne le pensais.Voici le code en C++/MFC, vous pouvez le traduire dans l'environnement de votre choix.

CFileDialog dlg(true, NULL, "Filename will be ignored", OFN_HIDEREADONLY | OFN_NOVALIDATE | OFN_PATHMUSTEXIST | OFN_READONLY, NULL, this);
dlg.DoModal();

Modifier 2 : Cela devrait être la traduction en C#, mais je ne parle pas couramment C# alors ne me tirez pas dessus si cela ne fonctionne pas.

OpenFileDialog openFileDialog1 = new OpenFileDialog();

openFileDialog1.FileName = "Filename will be ignored";
openFileDialog1.CheckPathExists = true;
openFileDialog1.ShowReadOnly = false;
openFileDialog1.ReadOnlyChecked = true;
openFileDialog1.CheckFileExists = false;
openFileDialog1.ValidateNames = false;

if(openFileDialog1.ShowDialog() == DialogResult.OK)
{
    // openFileDialog1.FileName should contain the folder and a dummy filename
}

Modifier 3 : Enfin, j'ai regardé la boîte de dialogue en question, dans Visual Studio 2005 (je n'y avais pas accès plus tôt). Ce n'est pas la boîte de dialogue standard d'ouverture de fichier ! Si vous inspectez les fenêtres dans Spy++ et les comparez à un fichier standard ouvert, vous verrez que les noms de structure et de classe ne correspondent pas.Lorsque vous regardez attentivement, vous pouvez également remarquer certaines différences entre le contenu des boîtes de dialogue.Ma conclusion est que Microsoft a complètement remplacé la boîte de dialogue standard de Visual Studio pour lui donner cette fonctionnalité.Ma solution ou quelque chose de similaire sera aussi proche que possible, à moins que vous ne souhaitiez coder la vôtre à partir de zéro.

Ok, permettez-moi d'essayer de connecter le premier point ;-) Jouer un peu avec Spy ++ ou WinSpector montre que la zone de texte du dossier dans l'emplacement du projet VS est une personnalisation de la boîte de dialogue standard.Ce n'est pas le même champ que la zone de texte du nom de fichier dans une boîte de dialogue de fichier standard telle que celle du Bloc-notes.

À partir de là, je suppose que VS masque les zones de texte/combobox de nom de fichier et de type de fichier et utilise un modèle de boîte de dialogue personnalisé pour ajouter sa propre partie au bas de la boîte de dialogue.

MODIFIER:Voici un exemple d'une telle personnalisation et comment le faire (dans Win32.pas .NET) :

m_ofn est la structure OPENFILENAME qui sous-tend la boîte de dialogue de fichier.Ajoutez ces 2 lignes :

  m_ofn.lpTemplateName = MAKEINTRESOURCE(IDD_FILEDIALOG_IMPORTXLIFF);
  m_ofn.Flags |= OFN_ENABLETEMPLATE;

où IDD_FILEDIALOG_IMPORTXLIFF est un modèle de boîte de dialogue personnalisé qui sera ajouté au bas de la boîte de dialogue.Voir la partie en rouge ci-dessous.alt text
(source: apptranslator.com)

Dans ce cas, la partie personnalisée n'est qu'un label + un lien hypertexte mais il peut s'agir de n'importe quelle boîte de dialogue.Il pourrait contenir un bouton OK qui nous permettrait de valider la sélection du dossier uniquement.

Mais comment pourrions-nous nous débarrasser de certains contrôles dans la partie standard de la boîte de dialogue, je ne sais pas.

Plus de détails dans cet article MSDN.

Vous pouvez sous-classer la boîte de dialogue de fichier et accéder à tous ses contrôles.Chacun possède un identifiant qui peut être utilisé pour obtenir son handle de fenêtre.Vous pouvez ensuite les afficher et les masquer, recevoir des messages de leur part sur les modifications de sélection, etc.etc.Tout dépend de l'effort que vous souhaitez fournir.

Nous avons réalisé le nôtre en utilisant la prise en charge de la classe WTL et avons personnalisé la boîte de dialogue de fichier pour inclure une barre de lieux personnalisée et des vues COM de plug-in.

MSDN fournit des informations sur la façon de procéder avec Win32, cet article de CodeProject comprend un exemple, et cet article CodeProject fournit un exemple .NET.

Vous pouvez utiliser du code comme celui-ci

  • Le filtre cache les fichiers
  • Le nom du fichier est masquer le premier texte

Pour masquer de manière avancée la zone de texte du nom de fichier, vous devez examinerOpenFileDialogEx

Le code:

{
    openFileDialog2.FileName = "\r";
    openFileDialog1.Filter = "folders|*.neverseenthisfile";
    openFileDialog1.CheckFileExists = false;
    openFileDialog1.CheckPathExists = false;
}

Je suppose que vous êtes sous Vista et que vous utilisez VS2008 ?Dans ce cas, je pense que Option FOS_PICKFOLDERS est utilisé lors de l'appel de la boîte de dialogue de fichier Vista IFileDialog.Je crains que dans le code .NET, cela implique beaucoup de code d'interopérabilité P/Invoke pour fonctionner.

Première solution

J'ai développé ceci comme une version nettoyée de Boîte de dialogue de sélection de dossier de style .NET Win 7 par Bill Seddon de lyquidity.com (Je n'ai aucune affiliation).(J'ai appris son code grâce à une autre réponse sur cette page).J'ai écrit la mienne parce que sa solution nécessite une classe Reflection supplémentaire qui n'est pas nécessaire à cet objectif ciblé, utilise un contrôle de flux basé sur les exceptions et ne met pas en cache les résultats de ses appels de réflexion.Notez que le statique imbriqué VistaDialog la classe est telle que ses variables de réflexion statiques n'essaient pas d'être remplies si la classe Show la méthode n’est jamais appelée.Il revient à la boîte de dialogue pré-Vista s'il n'est pas dans une version Windows suffisamment élevée.Devrait fonctionner sous Windows 7, 8, 9, 10 et versions ultérieures (théoriquement).

using System;
using System.Reflection;
using System.Windows.Forms;

namespace ErikE.Shuriken {
    /// <summary>
    /// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions
    /// </summary>
    public class FolderSelectDialog {
        private string _initialDirectory;
        private string _title;
        private string _fileName = "";

        public string InitialDirectory {
            get { return string.IsNullOrEmpty(_initialDirectory) ? Environment.CurrentDirectory : _initialDirectory; }
            set { _initialDirectory = value; }
        }
        public string Title {
            get { return _title ?? "Select a folder"; }
            set { _title = value; }
        }
        public string FileName { get { return _fileName; } }

        public bool Show() { return Show(IntPtr.Zero); }

        /// <param name="hWndOwner">Handle of the control or window to be the parent of the file dialog</param>
        /// <returns>true if the user clicks OK</returns>
        public bool Show(IntPtr hWndOwner) {
            var result = Environment.OSVersion.Version.Major >= 6
                ? VistaDialog.Show(hWndOwner, InitialDirectory, Title)
                : ShowXpDialog(hWndOwner, InitialDirectory, Title);
            _fileName = result.FileName;
            return result.Result;
        }

        private struct ShowDialogResult {
            public bool Result { get; set; }
            public string FileName { get; set; }
        }

        private static ShowDialogResult ShowXpDialog(IntPtr ownerHandle, string initialDirectory, string title) {
            var folderBrowserDialog = new FolderBrowserDialog {
                Description = title,
                SelectedPath = initialDirectory,
                ShowNewFolderButton = false
            };
            var dialogResult = new ShowDialogResult();
            if (folderBrowserDialog.ShowDialog(new WindowWrapper(ownerHandle)) == DialogResult.OK) {
                dialogResult.Result = true;
                dialogResult.FileName = folderBrowserDialog.SelectedPath;
            }
            return dialogResult;
        }

        private static class VistaDialog {
            private const string c_foldersFilter = "Folders|\n";

            private const BindingFlags c_flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            private readonly static Assembly s_windowsFormsAssembly = typeof(FileDialog).Assembly;
            private readonly static Type s_iFileDialogType = s_windowsFormsAssembly.GetType("System.Windows.Forms.FileDialogNative+IFileDialog");
            private readonly static MethodInfo s_createVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("CreateVistaDialog", c_flags);
            private readonly static MethodInfo s_onBeforeVistaDialogMethodInfo = typeof(OpenFileDialog).GetMethod("OnBeforeVistaDialog", c_flags);
            private readonly static MethodInfo s_getOptionsMethodInfo = typeof(FileDialog).GetMethod("GetOptions", c_flags);
            private readonly static MethodInfo s_setOptionsMethodInfo = s_iFileDialogType.GetMethod("SetOptions", c_flags);
            private readonly static uint s_fosPickFoldersBitFlag = (uint) s_windowsFormsAssembly
                .GetType("System.Windows.Forms.FileDialogNative+FOS")
                .GetField("FOS_PICKFOLDERS")
                .GetValue(null);
            private readonly static ConstructorInfo s_vistaDialogEventsConstructorInfo = s_windowsFormsAssembly
                .GetType("System.Windows.Forms.FileDialog+VistaDialogEvents")
                .GetConstructor(c_flags, null, new[] { typeof(FileDialog) }, null);
            private readonly static MethodInfo s_adviseMethodInfo = s_iFileDialogType.GetMethod("Advise");
            private readonly static MethodInfo s_unAdviseMethodInfo = s_iFileDialogType.GetMethod("Unadvise");
            private readonly static MethodInfo s_showMethodInfo = s_iFileDialogType.GetMethod("Show");

            public static ShowDialogResult Show(IntPtr ownerHandle, string initialDirectory, string title) {
                var openFileDialog = new OpenFileDialog {
                    AddExtension = false,
                    CheckFileExists = false,
                    DereferenceLinks = true,
                    Filter = c_foldersFilter,
                    InitialDirectory = initialDirectory,
                    Multiselect = false,
                    Title = title
                };

                var iFileDialog = s_createVistaDialogMethodInfo.Invoke(openFileDialog, new object[] { });
                s_onBeforeVistaDialogMethodInfo.Invoke(openFileDialog, new[] { iFileDialog });
                s_setOptionsMethodInfo.Invoke(iFileDialog, new object[] { (uint) s_getOptionsMethodInfo.Invoke(openFileDialog, new object[] { }) | s_fosPickFoldersBitFlag });
                var adviseParametersWithOutputConnectionToken = new[] { s_vistaDialogEventsConstructorInfo.Invoke(new object[] { openFileDialog }), 0U };
                s_adviseMethodInfo.Invoke(iFileDialog, adviseParametersWithOutputConnectionToken);

                try {
                    int retVal = (int) s_showMethodInfo.Invoke(iFileDialog, new object[] { ownerHandle });
                    return new ShowDialogResult {
                        Result = retVal == 0,
                        FileName = openFileDialog.FileName
                    };
                }
                finally {
                    s_unAdviseMethodInfo.Invoke(iFileDialog, new[] { adviseParametersWithOutputConnectionToken[1] });
                }
            }
        }

        // Wrap an IWin32Window around an IntPtr
        private class WindowWrapper : IWin32Window {
            private readonly IntPtr _handle;
            public WindowWrapper(IntPtr handle) { _handle = handle; }
            public IntPtr Handle { get { return _handle; } }
        }
    }
}

Il est utilisé ainsi dans un Windows Form :

var dialog = new FolderSelectDialog {
    InitialDirectory = musicFolderTextBox.Text,
    Title = "Select a folder to import music from"
};
if (dialog.Show(Handle)) {
    musicFolderTextBox.Text = dialog.FileName;
}

Vous pouvez bien sûr jouer avec ses options et les propriétés qu'il expose.Par exemple, il permet la sélection multiple dans la boîte de dialogue de style Vista.

Deuxième solution

Simon Mourier a donné une réponse qui montre comment faire exactement le même travail en utilisant l'interopérabilité directement avec l'API Windows, bien que sa version doive être complétée pour utiliser l'ancienne boîte de dialogue de style si vous êtes dans une ancienne version de Windows.Malheureusement, je n'avais pas encore trouvé son message lorsque j'ai élaboré ma solution.Nommez votre poison !

Essayez celui-ci de Projet de code (crédit à Nitron) :

Je pense que c'est la même boîte de dialogue dont vous parlez - peut-être que cela aiderait si vous ajoutiez une capture d'écran ?

bool GetFolder(std::string& folderpath, const char* szCaption=NULL, HWND hOwner=NULL)
{
    bool retVal = false;

    // The BROWSEINFO struct tells the shell how it should display the dialog.
    BROWSEINFO bi;
    memset(&bi, 0, sizeof(bi));

    bi.ulFlags   = BIF_USENEWUI;
    bi.hwndOwner = hOwner;
    bi.lpszTitle = szCaption;

    // must call this if using BIF_USENEWUI
    ::OleInitialize(NULL);

    // Show the dialog and get the itemIDList for the selected folder.
    LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);

    if(pIDL != NULL)
    {
        // Create a buffer to store the path, then get the path.
        char buffer[_MAX_PATH] = {'\0'};
        if(::SHGetPathFromIDList(pIDL, buffer) != 0)
        {
            // Set the string value.
            folderpath = buffer;
            retVal = true;
        }       

        // free the item id list
        CoTaskMemFree(pIDL);
    }

    ::OleUninitialize();

    return retVal;
}

Sur Vista, vous pouvez utiliser IFileDialog avec l'option FOS_PICKFOLDERS définie.Cela entraînera l'affichage d'une fenêtre de type OpenFileDialog dans laquelle vous pourrez sélectionner des dossiers :

var frm = (IFileDialog)(new FileOpenDialogRCW());
uint options;
frm.GetOptions(out options);
options |= FOS_PICKFOLDERS;
frm.SetOptions(options);

if (frm.Show(owner.Handle) == S_OK) {
    IShellItem shellItem;
    frm.GetResult(out shellItem);
    IntPtr pszString;
    shellItem.GetDisplayName(SIGDN_FILESYSPATH, out pszString);
    this.Folder = Marshal.PtrToStringAuto(pszString);
}

Pour les anciens Windows, vous pouvez toujours recourir à une astuce en sélectionnant n'importe quel fichier dans un dossier.

Un exemple fonctionnel qui fonctionne sur .NET Framework 2.0 et versions ultérieures peut être trouvé ici.

Vous pouvez utiliser du code comme celui-ci

Le filtre est une chaîne vide.Le nom du fichier est AnyName mais n'est pas vide

        openFileDialog.FileName = "AnyFile";
        openFileDialog.Filter = string.Empty;
        openFileDialog.CheckFileExists = false;
        openFileDialog.CheckPathExists = false;

Le Boîtes de dialogue Ookii pour WPF La bibliothèque a une classe qui fournit une implémentation d'une boîte de dialogue de navigateur de dossiers pour WPF.

https://github.com/caioproiete/ookii-dialogs-wpf

enter image description here

Il existe également une version qui fonctionne avec Formulaires Windows.

Je sais que la question portait sur la configuration de OpenFileDialog mais vu que Google m'a amené ici, je peux aussi bien souligner que si vous recherchez UNIQUEMENT des dossiers, vous devriez utiliser un FolderBrowserDialog Au lieu de cela, comme répondu par une autre question SO ci-dessous

Comment spécifier le chemin à l'aide de la boîte de dialogue d'ouverture de fichier dans vb.net ?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top