Come si configura un OpenFileDialog per selezionare le cartelle?

In VS .NET, quando si seleziona una cartella per un progetto, viene visualizzata una finestra di dialogo simile a OpenFileDialog o SaveFileDialog, ma configurata per accettare solo le cartelle. Da quando l’ho visto ho voluto sapere come è fatto. Sono a conoscenza di FolderBrowserDialog, ma non mi è mai piaciuta la finestra di dialogo. Inizia troppo piccolo e non mi permette di sfruttare la possibilità di digitare un percorso.

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

Vorrei anche riaffermare 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. Dicendomi di usare solo FolderBrowserDialog mi aiuta a mantenere un’esperienza di interfaccia utente coerente, ma non soddisfa la mia curiosità, quindi non conterà come risposta.

Neanche una cosa specifica per Vista; Ho visto questa finestra da VS .NET 2003, quindi è fattibile in Win2k e WinXP. Questo è meno di un “Voglio sapere il modo corretto per fare questa” domanda e più di una domanda “Sono stato curioso di questo da quando ho voluto farlo in VS 2003”. Capisco che la finestra di dialogo di Vista ha un’opzione per farlo, ma funziona in XP, quindi so che 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 puntato alla personalizzazione del dialogo (che è certamente antipatico da .NET ma funziona) e Mark Ransom per aver capito che MS probabilmente ha lanciato una finestra di dialogo personalizzata per questa attività.

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

Se si imposta il suo valore AcceptFiles su false, allora funziona solo in modalità accetta cartelle.

Puoi scaricare la fonte da GitHub qui

C’è il pacchetto di codice API di Windows. Ha un sacco di cose relative alla shell, inclusa la class CommonOpenFileDialog (nello spazio dei nomi Microsoft.WindowsAPICodePack.Dialogs ). Questa è la soluzione perfetta: la solita finestra di dialogo aperta con solo le cartelle visualizzate.

Ecco un esempio di come usarlo:

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

Sfortunatamente Microsoft non spedisce più questo pacchetto, ma diverse persone hanno caricato binari non ufficiali su NuGet. Un esempio può essere trovato qui . Questo pacchetto è solo la roba specifica della shell. Se ne hai bisogno, lo stesso utente ha diversi altri pacchetti che offrono più funzionalità presenti nel pacchetto originale.

Puoi utilizzare FolderBrowserDialogEx , una derivata riutilizzabile del FolderBrowserDialog incorporato. Questo ti permette di digitare un percorso, anche un percorso UNC. Puoi anche cercare computer o stampanti con esso. Funziona proprio come il FBD integrato, ma … meglio.

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

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

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 pacchetto Ookii.Dialogs contiene un wrapper gestito attorno alla nuova finestra di dialogo del browser delle cartelle (Vista). Si degrada anche con grazia su sistemi operativi precedenti.

Meglio usare il FolderBrowserDialog per quello.

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

Dopo ore di ricerche ho trovato questa risposta da parte di leetNightShade a una soluzione funzionante .

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

  1. E ‘semplice da usare. Richiede solo di includere due file (che possono essere combinati in uno solo) nel progetto.
  2. Torna al FolderBrowserDialog standard quando viene utilizzato su XP o sistemi precedenti.
  3. L’autore concede il permesso di utilizzare il codice per qualsiasi scopo tu ritenga opportuno.

    Non c’è una licenza in quanto tu sei libero di prendere e fare con il codice quello che vuoi.

Scarica il codice qui .

OK, permettimi di provare a colbind il primo punto 😉 Giocare un po ‘con Spy ++ o Winspector mostra che la casella di testo Folder nella VS Project Location è una personalizzazione della finestra di dialogo standard. Non è lo stesso campo della casella di testo del nome del file in una finestra di dialogo standard come quella del Blocco note.

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

EDIT: Ecco un esempio di tale personalizzazione e come farlo (in Win32, non in .NET):

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

  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 http://sofit.miximages.com/.net/www.apptranslator.com

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

Ma come potremmo sbarazzarci di alcuni dei controlli nella parte standard del dialogo, non lo so.

Maggiori dettagli in questo articolo MSDN .

Exact Audio Copy funziona in questo modo su Windows XP. Viene visualizzata la finestra di dialogo di apertura del file standard, ma il campo del nome file contiene il testo “Nome file verrà ignorato”.

Sto solo indovinando qui, ma sospetto che la stringa venga iniettata 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 le flag di dialogo sono impostate per non verificare l’esistenza del file, la finestra di dialogo può essere chiusa normalmente.

Modifica: questo è molto più facile 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 io non parlo fluentemente in C # quindi non spararmi 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 guardato l’effettiva finestra di dialogo in questione, in Visual Studio 2005 (non ho avuto accesso ad esso in precedenza). Non è la finestra di dialogo standard di apertura dei file! Se ispezionate le windows in Spy ++ e le confrontate con un file standard aperto, vedrete che i nomi di struttura e class non corrispondono. Quando guardi da vicino, puoi anche notare alcune differenze tra i contenuti delle windows di dialogo. La mia conclusione è che Microsoft ha completamente sostituito la finestra di dialogo standard in Visual Studio per dargli questa possibilità. La mia soluzione o qualcosa di simile sarà il più vicino ansible, a meno che tu non voglia codificarti da zero.

Puoi creare una sottoclass della finestra di dialogo dei file e accedere a tutti i suoi controlli. Ognuno ha un identificatore che può essere utilizzato per ottenere il suo handle di finestra. Puoi quindi mostrarli e nasconderli, ricevere messaggi da loro su modifiche alla selezione, ecc. Ecc. Tutto dipende da quanto impegno vuoi fare.

Abbiamo utilizzato il nostro supporto per la class WTL e personalizzato la finestra di dialogo del file per includere una barra dei luoghi personalizzata e le viste COM dei plug-in.

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

Puoi usare un codice come questo

  • Il filtro è file nascosti
  • Il nome file è nascosto come primo testo

Per hide la casella di testo avanzata per il nome del file è necessario guardare OpenFileDialogEx

Il codice:

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

Presumo che tu sia su Vista usando VS2008? In tal caso, penso che l’ opzione FOS_PICKFOLDERS venga utilizzata quando si richiama la finestra di dialogo del file Vista IFileDialog . Temo che nel codice .NET questo implichi un sacco di codice di interoperabilità gn / P / Invoke per funzionare.

Prima soluzione

Ho sviluppato questo come una versione ripulita di .NET Win 7 dialogo di selezione cartella in stile di Bill Seddon di lyquidity.com (non ho affiliazione). (Ho saputo del suo codice da un’altra risposta in questa pagina ). Ho scritto il mio perché la sua soluzione richiede una class Reflection aggiuntiva che non è necessaria per questo scopo focalizzato, utilizza il controllo del stream basato sull’eccezione, non memorizza nella cache i risultati delle sue chiamate di riflessione. Si noti che la class VistaDialog statica nidificata è tale che le sue variabili di riflessione statica non cercano di essere popolate se il metodo Show non viene mai chiamato. Torna alla finestra di anteprima di Vista, se non in una versione di Windows sufficientemente alta. Dovrebbe funzionare in Windows 7, 8, 9, 10 e successivi (in teoria).

 using System; using System.Reflection; using System.Windows.Forms; namespace ErikE.Shuriken { ///  /// Present the Windows Vista-style open file dialog to select a folder. Fall back for older Windows Versions ///  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); } /// Handle of the control or window to be the parent of the file dialog /// true if the user clicks OK 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 il multiselect nella finestra di dialogo in stile Vista.

Seconda soluzione

Simon Mourier ha fornito una risposta che mostra come eseguire esattamente lo stesso lavoro utilizzando l’interoperabilità con l’API di Windows direttamente, sebbene la sua versione debba essere integrata per utilizzare la finestra di dialogo stile precedente se in una versione precedente di Windows. Sfortunatamente, non avevo ancora trovato il suo posto 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 cui stai parlando – forse sarebbe d’aiuto se aggiungi 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 il set di opzioni FOS_PICKFOLDERS. Ciò causerà la visualizzazione della finestra simile a OpenFileDialog in cui è ansible 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 meno recenti puoi sempre ricorrere a trucchi per selezionare qualsiasi file nella cartella.

L’esempio di lavoro che funziona su .NET Framework 2.0 e versioni successive può essere trovato qui .

Puoi usare un codice come questo

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

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

So che la domanda era sulla configurazione di OpenFileDialog ma visto che Google mi ha portato qui posso anche sottolineare che se stai cercando SOLO le cartelle dovresti usare un FolderBrowserDialog invece come risposta da un’altra domanda SO sotto

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