¿Cómo se configura un OpenFileDialog para seleccionar carpetas?

En VS .NET, cuando selecciona una carpeta para un proyecto, se muestra un cuadro de diálogo que se parece a un OpenFileDialog o SaveFileDialog, pero está configurado para aceptar solo carpetas. Desde que lo he visto, he querido saber cómo se hace. Conozco el FolderBrowserDialog, pero nunca me ha gustado ese diálogo. Comienza demasiado pequeño y no me deja aprovechar la posibilidad de escribir un camino.

Estoy casi seguro de que no hay forma de hacerlo desde .NET, pero tengo curiosidad por saber cómo hacerlo desde un código no administrado también. Si no se vuelve a implementar completamente el diálogo desde cero, ¿cómo se modifica el diálogo para tener este comportamiento?

También me gustaría reiterar que conozco el FolderBrowserDialog, pero a veces no me gusta usarlo, además de ser realmente curioso sobre cómo configurar un diálogo de esta manera. Decirme que use el FolderBrowserDialog me ayuda a mantener una experiencia de interfaz de usuario coherente pero no satisface mi curiosidad, por lo que no contará como respuesta.

Tampoco es algo específico de Vista; He estado viendo este diálogo desde VS.NET 2003, por lo que es factible en Win2k y WinXP. No se trata tanto de una pregunta de “Quiero saber la forma correcta de hacer esto” como de una pregunta “Tengo curiosidad sobre esto desde que primero quise hacerlo en VS 2003”. Entiendo que el cuadro de diálogo de Vista tiene una opción para hacer esto, pero ha estado funcionando en XP, así que sé que hicieron algo para que funcione. Las respuestas específicas de Vista no son respuestas, porque Vista no existe en el contexto de la pregunta.

Actualización: Estoy aceptando la respuesta de Scott Wisniewski porque viene con una muestra de trabajo, pero creo que Serge merece crédito por apuntar a la personalización de diálogo (que es ciertamente desagradable de .NET pero funciona) y Mark Ransom por descubrir que MS probablemente rodó un diálogo personalizado para esta tarea.

Tengo un diálogo que escribí llamado un cuadro de diálogo OpenFileOrFolder que le permite abrir una carpeta o un archivo.

Si configura su valor de AcceptFiles en falso, entonces solo funciona en modo de aceptar carpetas.

Puedes descargar la fuente de GitHub aquí

Existe el paquete de código API de Windows. Tiene muchas cosas relacionadas con el shell, incluida la clase CommonOpenFileDialog (en el espacio de nombres Microsoft.WindowsAPICodePack.Dialogs ). Esta es la solución perfecta: el diálogo abierto habitual con solo las carpetas mostradas.

Aquí hay un ejemplo de cómo usarlo:

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

Desafortunadamente Microsoft ya no envía este paquete, pero varias personas han subido binarios extraoficialmente a NuGet. Un ejemplo se puede encontrar aquí . Este paquete es solo el material específico del shell. Si lo necesita, el mismo usuario tiene varios otros paquetes que ofrecen más funcionalidad presente en el paquete original.

Puede usar FolderBrowserDialogEx – una derivada reutilizable del FolderBrowserDialog incorporado. Esta le permite escribir una ruta, incluso una ruta UNC. También puede buscar computadoras o impresoras con él. Funciona igual que el FBD incorporado, pero … mejor.

(EDITAR: Debería haber señalado que este diálogo se puede configurar para seleccionar archivos o carpetas).

Código fuente completo (un módulo corto C #). Gratis. Licencia MS-Public.

Código para 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; } 

El paquete Ookii.Dialogs contiene un contenedor gestionado alrededor del nuevo diálogo del navegador de carpetas (estilo Vista). También se degrada graciosamente en los sistemas operativos más antiguos.

Es mejor usar el FolderBrowserDialog para eso.

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

Después de horas de búsqueda encontré esta respuesta de leetNightShade a una solución de trabajo .

Hay tres cosas que creo que hacen que esta solución sea mucho mejor que todas las demás.

  1. Es simple de usar. Solo requiere que incluya dos archivos (que se pueden combinar de todos modos) en su proyecto.
  2. Se recurre al FolderBrowserDialog estándar cuando se usa en sistemas XP o anteriores.
  3. El autor otorga permiso para usar el código para cualquier propósito que usted considere apropiado.

    No hay licencia como tal que pueda tomar y hacer con el código.

Descargue el código aquí .

OK, déjame intentar conectar el primer punto 😉 Jugar un poco con Spy ++ o Winspector muestra que el cuadro de texto Carpeta en la Ubicación del proyecto VS es una personalización del cuadro de diálogo estándar. No es el mismo campo que el cuadro de texto de nombre de archivo en un cuadro de diálogo de archivo estándar como el del Bloc de notas.

A partir de ahí, supongo, VS oculta el nombre de archivo y las cajas de texto / cuadros combinados de tipo de archivo y utiliza una plantilla de diálogo personalizada para agregar su propia parte en la parte inferior del cuadro de diálogo.

EDITAR: Aquí hay un ejemplo de dicha personalización y cómo hacerlo (en Win32. Not .NET):

m_ofn es la estructura OPENFILENAME que se encuentra debajo del cuadro de diálogo del archivo. Agregue estas 2 líneas:

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

donde IDD_FILEDIALOG_IMPORTXLIFF es una plantilla de diálogo personalizada que se agregará en la parte inferior del cuadro de diálogo. Vea la parte en rojo a continuación. texto alternativo http://sofes.miximages.com/.net/www.apptranslator.com

En este caso, la parte personalizada es solo una etiqueta + un hipervínculo, pero podría ser cualquier diálogo. Podría contener un botón Aceptar que nos permitiría validar solo la selección de carpeta.

Pero no sé cómo nos desharíamos de algunos de los controles en la parte estándar del diálogo.

Más detalles en este artículo de MSDN .

Exact Audio Copy funciona de esta manera en Windows XP. Se muestra el diálogo de abrir archivo estándar, pero el campo de nombre de archivo contiene el texto “El nombre de archivo será ignorado”.

Solo estoy adivinando aquí, pero sospecho que la cadena se inyecta en el control de edición del cuadro combinado cada vez que se realiza un cambio significativo en el cuadro de diálogo. Siempre que el campo no esté en blanco, y los indicadores de diálogo estén configurados para no verificar la existencia del archivo, el diálogo se puede cerrar normalmente.

Editar: esto es mucho más fácil de lo que pensaba. Aquí está el código en C ++ / MFC, puede traducirlo al entorno de su elección.

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

Editar 2: Esta debería ser la traducción a C #, pero no soy fluida en C #, así que no me dispares si no funciona.

 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 } 

Editar 3: Finalmente miré el diálogo real en cuestión, en Visual Studio 2005 (no tuve acceso a él antes). ¡No es el diálogo estándar de abrir archivo! Si inspecciona las ventanas en Spy ++ y las compara con un archivo estándar abierto, verá que la estructura y los nombres de clase no coinciden. Cuando mira de cerca, también puede detectar algunas diferencias entre los contenidos de los diálogos. Mi conclusión es que Microsoft reemplazó completamente el diálogo estándar en Visual Studio para darle esta capacidad. Mi solución o algo similar será lo más cercano que pueda obtener, a menos que esté dispuesto a codificar el suyo desde cero.

Puede crear una subclase del diálogo de archivo y obtener acceso a todos sus controles. Cada uno tiene un identificador que se puede usar para obtener su identificador de ventana. Luego puede mostrarlos y ocultarlos, recibir mensajes de ellos sobre cambios de selección, etc. Todo depende de cuánto esfuerzo desee realizar.

Hicimos la nuestra usando soporte de clase WTL y personalizamos el diálogo de archivo para incluir una barra de lugares personalizada y vistas COM de complemento.

MSDN proporciona información sobre cómo hacer esto usando Win32, este artículo de CodeProject incluye un ejemplo , y este artículo de CodeProject proporciona un ejemplo de .NET .

Puedes usar un código como este

  • El filtro es archivos ocultos
  • El nombre de archivo es ocultar el primer texto

Para ocultar de forma avanzada el cuadro de texto de nombre de archivo, debe mirar OpenFileDialogEx

El código:

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

¿Asumo que estás en Vista usando VS2008? En ese caso, creo que la opción FOS_PICKFOLDERS se está utilizando al llamar al diálogo de archivo de Vista IFileDialog . Me temo que en el código .NET esto implicaría un montón de código de interferencia de P / Invoke para trabajar.

Primera solución

Desarrollé esto como una versión limpiada del diálogo de selección de carpetas .NET Win 7-style por Bill Seddon de lyquidity.com (no tengo afiliación). (Me enteré de su código de otra respuesta en esta página ). Escribí el mío porque su solución requiere una clase Reflection adicional que no es necesaria para este propósito específico, usa control de flujo basado en excepciones, no almacena en caché los resultados de sus llamadas de reflexión. Tenga en cuenta que la clase VistaDialog estática anidada es para que sus variables de reflexión estática no intenten rellenarse si nunca se llama al método Show . Se vuelve al cuadro de diálogo anterior a Vista si no se encuentra en una versión suficientemente alta de Windows. Debería funcionar en Windows 7, 8, 9, 10 y superior (teóricamente).

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

Se usa como tal en 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; } 

Por supuesto, puede jugar con sus opciones y qué propiedades expone. Por ejemplo, permite multiselección en el cuadro de diálogo Vista-style.

Segunda solución

Simon Mourier dio una respuesta que muestra cómo hacer exactamente el mismo trabajo utilizando la interoperabilidad directamente con la API de Windows, aunque su versión tendría que completarse para usar el diálogo de estilo anterior si es una versión anterior de Windows. Lamentablemente, todavía no había encontrado su publicación cuando trabajé en mi solución. ¡Nombra tu veneno!

Pruebe este de Codeproject (crédito a Nitron):

Creo que es el mismo diálogo del que hablas, tal vez sería útil si agregas una captura de pantalla.

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

En Vista puede usar IFileDialog con la opción FOS_PICKFOLDERS establecida. Eso causará la visualización de una ventana similar a OpenFileDialog donde puede seleccionar carpetas:

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

Para Windows más antiguo, siempre puede recurrir a un truco al seleccionar cualquier archivo en la carpeta.

El ejemplo de trabajo que funciona en .NET Framework 2.0 y posterior se puede encontrar aquí .

Puedes usar un código como este

El filtro es una cadena vacía. El nombre del archivo es AnyName pero no está en blanco

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

Sé que la pregunta era sobre la configuración de OpenFileDialog pero al ver que Google me trajo aquí, puedo señalar que si SOLAMENTE está buscando carpetas, debería usar un FolderBrowserDialog lugar de responder a otra pregunta SO a continuación

¿Cómo especificar la ruta usando el diálogo de abrir archivo en vb.net?