Domanda

In VS .NET, quando si seleziona una cartella per un progetto, viene visualizzata una finestra di dialogo che assomiglia a OpenFileDialog o SaveFileDialog, ma è impostata per accettare solo cartelle.Da quando l'ho visto ho voluto sapere come si fa.Sono a conoscenza di FolderBrowserDialog, ma non mi è mai piaciuta molto quella finestra di dialogo.Inizia troppo piccolo e non mi consente di sfruttare la possibilità di digitare un percorso.

Sono quasi certo che ormai non esiste un modo per farlo da .NET, ma sono altrettanto curioso di sapere come farlo anche da codice non gestito.A meno di reimplementare completamente la finestra di dialogo da zero, come si modifica la finestra di dialogo per avere questo comportamento?

Vorrei anche ribadire che sono a conoscenza di FolderBrowserDialog ma a volte non mi piace usarlo, oltre ad essere sinceramente curioso di sapere come configurare una finestra di dialogo in questo modo.Dirmi di utilizzare semplicemente il FolderBrowserDialog mi aiuta a mantenere un'esperienza dell'interfaccia utente coerente ma non soddisfa la mia curiosità, quindi non verrà conteggiato come risposta.

Non è nemmeno una cosa specifica di Vista;Vedo questa finestra di dialogo da VS .NET 2003, quindi è fattibile in Win2k e WinXP.Questa è meno una domanda "Voglio sapere il modo corretto per farlo" e più una domanda "Sono stato curioso di questo da quando ho voluto farlo per la prima volta in VS 2003".Capisco che la finestra di dialogo dei file di Vista ha un'opzione per farlo, ma funziona in XP, quindi so che lo hanno fatto qualcosa per farlo funzionare.Le risposte specifiche per Vista non sono risposte, perché Vista non esiste nel contesto della domanda.

Aggiornamento:Accetto la risposta di Scott Wisniewski perché viene fornita con un campione funzionante, ma penso che Serge meriti credito per aver indicato la personalizzazione del dialogo (che è certamente sgradevole da .NET ma non è fa lavoro) e Mark Ransom per aver scoperto che MS probabilmente ha lanciato una finestra di dialogo personalizzata per questa attività.

È stato utile?

Soluzione

Ho una finestra di dialogo che ho scritto chiamata finestra di dialogo OpenFileOrFolder che ti consente di aprire una cartella o un file.

Se imposti il ​​valore AcceptFiles su false, funziona solo in modalità accetta cartella.

Puoi scaricare il sorgente da GitHub qui

Altri suggerimenti

È disponibile il pacchetto di codici API di Windows.Contiene un sacco di cose relative alla shell, incluso il file CommonOpenFileDialog classe (nella Microsoft.WindowsAPICodePack.Dialogs spazio dei nomi).Questa è la soluzione perfetta: la solita finestra di dialogo aperta con solo le cartelle visualizzate.

Ecco un esempio di come utilizzarlo:

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

Sfortunatamente Microsoft non fornisce più questo pacchetto, ma diverse persone hanno caricato ufficiosamente i file binari su NuGet.Si può trovare un esempio Qui.Questo pacchetto è solo materiale specifico per la shell.Se ne hai bisogno, lo stesso utente ha diversi altri pacchetti che offrono più funzionalità presenti nel pacchetto originale.

Puoi usare CartellaBrowserDialogEx -Un derivato riutilizzabile della cartella incorporata di rowerDialog.Questo ti permette di digitare un percorso, anche un percorso UNC.Puoi anche cercare computer o stampanti con esso.Funziona proprio come l'FBD integrato, ma...Meglio.

(MODIFICARE:Avrei dovuto sottolineare che questa finestra di dialogo può essere impostata per selezionare file o cartelle.)

Codice sorgente completo (un breve modulo C#).Gratuito.Licenza MS-Public.

Codice per usarlo:

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

IL Ookii.Dialogs Il pacchetto contiene un wrapper gestito attorno alla nuova finestra di dialogo del browser delle cartelle (in stile Vista).Inoltre si degrada con garbo sui sistemi operativi più vecchi.

Per questo è meglio usare il FolderBrowserDialog.

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

Dopo ore di ricerca ho trovato questa risposta di leetNightShade A una soluzione funzionante.

Ci sono tre cose che credo rendano questa soluzione molto migliore di tutte le altre.

  1. E 'semplice da usare. Richiede solo che tu includa due file (che possono comunque essere combinati in uno solo) nel tuo progetto.
  2. Si ritorna allo standard Finestra di dialogoBrowserCartella se utilizzato su sistemi XP o precedenti.
  3. L'autore concede il permesso di utilizzare il codice per qualsiasi scopo ritenga opportuno.

    Non esiste una licenza in quanto sei libero di prendere e fare con il codice ciò che vuoi.

Scarica il codice Qui.

Copia audio esatta funziona in questo modo su Windows XP.Viene visualizzata la finestra di dialogo standard di apertura file, ma il campo del nome file contiene il testo "Il nome file verrà ignorato".

Sto solo indovinando, ma sospetto che la stringa venga inserita nel controllo di modifica della casella combinata ogni volta che viene apportata una modifica significativa alla finestra di dialogo.Finché il campo non è vuoto e i flag della finestra di dialogo sono impostati per non verificare l'esistenza del file, la finestra di dialogo può essere chiusa normalmente.

Modificare: questo è molto più semplice di quanto pensassi.Ecco il codice in C++/MFC, puoi tradurlo nell'ambiente che preferisci.

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

Modifica 2: Questa dovrebbe essere la traduzione in C#, ma non parlo fluentemente C# quindi non sparatemi se non funziona.

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
}

Modifica 3: Infine ho esaminato la finestra di dialogo in questione, in Visual Studio 2005 (non vi avevo accesso prima). Non è la finestra di dialogo standard per l'apertura dei file! Se controlli le finestre in Spy++ e le confronti con un file standard aperto, vedrai che la struttura e i nomi delle classi non corrispondono.Se guardi da vicino, puoi anche notare alcune differenze tra i contenuti delle finestre di dialogo.La mia conclusione è che Microsoft ha completamente sostituito la finestra di dialogo standard in Visual Studio per dotarla di questa funzionalità.La mia soluzione o qualcosa di simile sarà il più vicino possibile, a meno che tu non sia disposto a codificarne una tua da zero.

Ok, lasciami provare a collegare il primo punto ;-) Giocare un po 'con Spy ++ o Winspector mostra che la casella di testo della cartella nella posizione del progetto VS è una personalizzazione della finestra di dialogo standard.Non è lo stesso campo della casella di testo del nome file in una finestra di dialogo file standard come quella del Blocco note.

Da lì in poi, immagino, VS nasconde le caselle di testo/combobox del nome file e del tipo di file e utilizza un modello di dialogo personalizzato per aggiungere la propria parte nella parte inferiore della finestra di dialogo.

MODIFICARE:Ecco un esempio di tale personalizzazione e come eseguirla (in Win32.non .NET):

m_ofn è la struttura OPENFILENAME che è alla base della finestra di dialogo del file.Aggiungi queste 2 righe:

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

dove IDD_FILEDIALOG_IMPORTXLIFF è un modello di finestra di dialogo personalizzato che verrà aggiunto nella parte inferiore della finestra di dialogo.Vedi la parte in rosso qui sotto.alt text
(fonte: apptranslator.com)

In questo caso la parte personalizzata è solo un'etichetta + un collegamento ipertestuale ma potrebbe essere una qualsiasi finestra di dialogo.Potrebbe contenere un pulsante OK che ci consentirebbe di convalidare solo la selezione della cartella.

Ma come potremmo eliminare alcuni controlli nella parte standard della finestra di dialogo, non lo so.

Maggiori dettagli in questo articolo di MSDN.

È possibile creare una sottoclasse della finestra di dialogo del file e ottenere l'accesso a tutti i suoi controlli.Ciascuno ha un identificatore che può essere utilizzato per ottenere il relativo handle di finestra.Puoi quindi mostrarli e nasconderli, ricevere messaggi da loro sui cambiamenti di selezione, ecc.eccetera.Dipende tutto da quanto impegno vuoi fare.

Abbiamo fatto il nostro utilizzando il supporto delle classi WTL e personalizzato la finestra di dialogo dei file per includere una barra delle posizioni personalizzata e visualizzazioni COM plug-in.

MSDN fornisce informazioni su come eseguire questa operazione utilizzando Win32, questo articolo di CodeProject include un esempio, E questo articolo di CodeProject fornisce un esempio .NET.

Puoi usare un codice come questo

  • Il filtro nasconde i file
  • Il nome del file è nascondi primo testo

Per nascondere in modo avanzato la casella di testo per il nome del file devi guardareOpenFileDialogEx

Il codice:

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

Presumo che tu stia utilizzando Vista utilizzando VS2008?In tal caso penso che il Opzione FOS_PICKFOLDERS viene utilizzato quando si richiama la finestra di dialogo del file Vista IFileDialog.Temo che nel codice .NET ciò comporterebbe un sacco di codice di interoperabilità P/Invoke nodoso per funzionare.

Prima Soluzione

L'ho sviluppato come una versione ripulita di Finestra di dialogo di selezione della cartella in stile .NET Win 7 di Bill Seddon di liquidity.com (Non ho alcuna affiliazione).(Ho appreso del suo codice da un'altra risposta in questa pagina).Ho scritto la mia perché la sua soluzione richiede una classe Reflection aggiuntiva che non è necessaria per questo scopo mirato, utilizza il controllo del flusso basato sulle eccezioni e non memorizza nella cache i risultati delle chiamate di riflessione.Si noti che il file static VistaDialog class è tale che le sue variabili di riflessione statiche non tentano di essere popolate se il file Show il metodo non viene mai chiamato.Ritorna alla finestra di dialogo pre-Vista se non in una versione di Windows sufficientemente avanzata.Dovrebbe funzionare su Windows 7, 8, 9, 10 e versioni successive (in teoria).

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

Viene utilizzato in questo modo in 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;
}

Ovviamente puoi giocare con le sue opzioni e quali proprietà espone.Ad esempio, consente la selezione multipla nella finestra di dialogo in stile Vista.

Seconda Soluzione

Simon Mourier ha dato una risposta che mostra come eseguire esattamente lo stesso lavoro utilizzando direttamente l'interoperabilità con l'API di Windows, anche se la sua versione dovrebbe essere integrata per utilizzare la finestra di dialogo di stile precedente se in una versione precedente di Windows.Sfortunatamente, non avevo ancora trovato il suo post quando ho elaborato la mia soluzione.Dai un nome al tuo veleno!

Prova questo da Codeproject (credito a Nitron):

Penso che sia la stessa finestra di dialogo di cui parli: forse sarebbe d'aiuto aggiungere uno screenshot?

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

Su Vista puoi usare IFileDialog con l'opzione FOS_PICKFOLDERS impostata.Ciò causerà la visualizzazione di una finestra simile a OpenFileDialog in cui è possibile selezionare le cartelle:

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

Per Windows più vecchi puoi sempre ricorrere al trucco selezionando qualsiasi file nella cartella.

È possibile trovare un esempio funzionante che funziona su .NET Framework 2.0 e versioni successive Qui.

Puoi usare un codice come questo

Il filtro è una stringa vuota.Il nome del file è AnyName ma non è vuoto

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

IL Finestre di dialogo Ookii per WPF La libreria ha una classe che fornisce un'implementazione di una finestra di dialogo del browser delle cartelle per WPF.

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

enter image description here

C'è anche una versione che funziona con Moduli Windows.

So che la domanda riguardava la configurazione di OpenFileDialog ma visto che Google mi ha portato qui posso anche sottolineare che se stai cercando SOLO cartelle dovresti usare a FolderBrowserDialog Invece, come risposto da un'altra domanda SO di seguito

Come specificare il percorso utilizzando la finestra di dialogo Apri file in vb.net?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top