2009-02-12 20 views
12

Mi aplicación (C#, .NET 3.5) genera archivos y, además de generar eventos que pueden detectarse y reaccionarse, quiero mostrar la carpeta de destino al usuario en una forma. La lista de archivos se muestra en la misma forma que otra información.Incrustar una instancia de File Explorer en un formulario de aplicación Windows Forms

Estoy usando una instancia del control WebBrowser (System.Windows.Forms.WebBrowser), luego navegando a la carpeta. Esta muestra algunos vista predeterminada de la ventana del explorador, con el panel de resumen de archivo de la izquierda y los archivos en los azulejos '(grande icono y texto) vista.

Por ejemplo,

wb.Navigate(@"c:\path\to\folder\"); 

Me gustaría suprimir el panel y para ver la lista de archivos en la vista Detalles. El usuario puede acceder a esto a través de un menú contextual con el botón derecho, pero me gustaría que aparezca automáticamente.

Preferiría no tener que construir mi propio TreeView, DataGridView o lo que sea; el control WebBrowser hace todas las actualizaciones y reorganizaciones y otras cosas 'gratis'.

¿Hay una manera mejor? ¿Un control diferente para usar o algunos argumentos adicionales para pasar al control?

Y si pudiera atrapar eventos (por ejemplo, archivos seleccionados/renombrados/doble clic, etc.), ¡entonces mejor!

+0

Lo que encontré útil es el (comercial) [componente ShellBrowser] (http://www.jam-software.com/shellbrowser_net/?language=EN). –

Respuesta

4

el fin de manejar renombrar, borrar y hacer otra personalización que necesita para escribir su propio explorador de archivos. El control WebBrowser no es adecuado para sus necesidades. Es solo una envoltura sobre el componente ActiveX.
Debe comprobar this codeproject article. Contiene una implementación de explorador de archivos. Hay pocos más muestras de explorador de archivos:
one
two

+0

Eso es lo que me preocupaba: la necesidad de agregar muchos códigos propios. ¡Espero que el navegador web pueda tener argumentos aprobados para salvar el esfuerzo de mis perezosos huesos! – Unsliced

0

Si desea abrir una ventana diferente para mostrar el contenido de la carpeta de destino, puede usar System.Windows.Forms.OpenFileDialog, o SaveFileDialog, o heredar de FileDialog y extenderlo.

Para permitir que el usuario seleccione una carpeta puede usar FolderBrowserDialog, aunque como usuario no me gusta ese control.

¿Esto ayuda o que no tienen que insertar un control en su forma?

Asaf

+0

Ggg ... ¿cómo lo integrará en su forma? Y Unsliced ​​quiere aquí mostrar una lista de archivos generados en lugar de abrir archivos o seleccionar la carpeta de destino. – zihotki

+0

Comencé y termino preguntando si * tiene * para estar en la misma forma. A partir de la pregunta, no está claro si está incrustado en un formulario * porque * está implementado con un WebBrowser, o porque la vista de archivo está junto a otra cosa. No hay una forma directa de insertar un diálogo en una forma. –

+0

Más bien tiene que estar en la misma forma que la información debe mostrarse junto con otras actualizaciones de progreso y estado. Es sorprendente que tengamos abrir/guardar archivo/crear carpetas de diálogo, pero ninguna para explorar explícitamente ... – Unsliced

8

ADVERTENCIA: puesto de largo con una gran cantidad de código.

Cuando navega por el control del navegador web a una carpeta del sistema de archivos, el control del navegador web aloja una ventana de vista de shell que a su vez aloja la vista de lista del explorador. De hecho, esto es exactamente lo mismo que el proceso Explorer, así como los diálogos de archivos e Internet Explorer. Esta ventana de shell no es un control de modo que no existen métodos que pueden ser llamados en él o eventos que pueden ser suscrito a pero pueden recibir mensajes de ventanas y puede ser sub-clasificado.

Resulta que la parte de su pregunta relacionada con la configuración automática de la vista es en realidad bastante fácil. En caso Navigated de su control del explorador web, simplemente encontrar el identificador de la ventana vista de shell y enviar un mensaje WM_COMMAND con una constante de cáscara en particular (SHVIEW_REPORT). Este es un comando no documentado pero se admite en todas las plataformas de Windows hasta e incluyendo Windows 2008 y es casi seguro que será en Windows 7.Algo de código para agregar a la forma de su navegador web demuestra esto:

private delegate int EnumChildProc(IntPtr hwnd, IntPtr lParam); 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, 
     IntPtr wParam, IntPtr lParam); 

    [DllImport("user32.dll", SetLastError = true)] 
    private static extern int EnumChildWindows(IntPtr hWndParent, 
     EnumChildProc lpEnumFunc, IntPtr lParam); 

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
    private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, 
     int nMaxCount); 

    private const int WM_COMMAND = 0x0111; 
    private const int SHVIEW_REPORT = 0x702C; 
    private const string SHELLVIEW_CLASS = "SHELLDLL_DefView"; 

    private IntPtr m_ShellView; 

    void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e) 
    { 
     m_ShellView = IntPtr.Zero; 
     EnumChildWindows(webBrowser1.Handle, EnumChildren, IntPtr.Zero); 
     if (m_ShellView != IntPtr.Zero) 
     { 
      SendMessage(m_ShellView, WM_COMMAND, (IntPtr)SHVIEW_REPORT, (IntPtr)0); 
     } 
    } 

    private int EnumChildren(IntPtr hwnd, IntPtr lParam) 
    { 
     int retval = 1; 

     StringBuilder sb = new StringBuilder(SHELLVIEW_CLASS.Length + 1); 
     int numChars = GetClassName(hwnd, sb, sb.Capacity); 
     if (numChars == SHELLVIEW_CLASS.Length) 
     { 
      if (sb.ToString(0, numChars) == SHELLVIEW_CLASS) 
      { 
       m_ShellView = hwnd; 
       retval = 0; 
      } 
     } 

     return retval; 
    } 

Cada vez que el navegador web se desplaza a una nueva ventana (incluyendo cuando una carpeta se abre desde dentro de la vista de explorador) una nueva ventana de vista de la cáscara se crea por lo el mensaje se debe volver a enviar a la nueva ventana en cada evento Navegado.

Para la segunda parte de su pregunta, desea recibir eventos de la vista de lista del explorador. Esto es bastante más difícil que la primera parte. Para hacer esto, necesitaría subclasificar la ventana de vista de lista y luego monitorear los mensajes de Windows para los que le interesan (como WM_LBUTTONDBLCLK). Para subclasificar una ventana, necesitaría crear su propia clase derivada de la clase NativeWindow y asignarle el identificador de la ventana que necesita supervisar. A continuación, puede anular su procedimiento de Ventana y manejar los diversos mensajes como desee. A continuación se muestra un ejemplo de creación de un evento de doble clic: es relativamente simple, pero obtener un amplio acceso a la vista de lista del explorador puede implicar mucho más trabajo de lo que está dispuesto a hacer.

Agregue esto a su forma:

private ExplorerListView m_Explorer; 

    void OnExplorerItemExecuted(object sender, ExecuteEventArgs e) 
    { 
     string msg = string.Format("Item to be executed: {0}{0}{1}", 
      Environment.NewLine, e.SelectedItem); 
     e.Cancel = (MessageBox.Show(msg, "", MessageBoxButtons.OKCancel) 
      == DialogResult.Cancel); 
    } 

y estas dos líneas al manejador Navigated evento (justo después de la SendMessage):

m_Explorer = new ExplorerListView(m_ShellView); 
    m_Explorer.ItemExecuted += OnExplorerItemExecuted; 

A continuación, agregue las siguientes clases:

class ExplorerListView : NativeWindow 
{ 

    public event EventHandler<ExecuteEventArgs> ItemExecuted; 

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

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
    private static extern IntPtr FindWindowEx(IntPtr hwndParent, 
     IntPtr hwndChildAfter, string lpszClass, string lpszWindow); 

    private const int WM_LBUTTONDBLCLK = 0x0203; 

    private const int LVM_GETNEXTITEM = 0x100C; 
    private const int LVM_GETITEMTEXT = 0x1073; 

    private const int LVNI_SELECTED = 0x0002; 

    private const string EXPLORER_LISTVIEW_CLASS = "SysListView32"; 

    public ExplorerListView(IntPtr shellViewHandle) 
    { 
     base.AssignHandle(FindWindowEx(shellViewHandle, IntPtr.Zero, 
      EXPLORER_LISTVIEW_CLASS, null)); 
     if (base.Handle == IntPtr.Zero) 
     { 
      throw new ArgumentException("Window supplied does not encapsulate an explorer window."); 
     } 
    } 


    protected override void WndProc(ref Message m) 
    { 
     switch (m.Msg) 
     { 
      case WM_LBUTTONDBLCLK: 
       if (OnItemExecution() != 0) return; 
       break; 
      default: 
       break; 
     } 
     base.WndProc(ref m); 
    } 

    private int OnItemExecution() 
    { 
     int cancel = 0; 
     ExecuteEventArgs args = new ExecuteEventArgs(GetSelectedItem()); 
     EventHandler<ExecuteEventArgs> temp = ItemExecuted; 
     if (temp != null) 
     { 
      temp(this, args); 
      if (args.Cancel) cancel = 1; 
     } 
     return cancel; 
    } 

    private string GetSelectedItem() 
    { 
     string item = null; 

     IntPtr pStringBuffer = Marshal.AllocHGlobal(2048); 
     IntPtr pItemBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(LVITEM))); 

     int selectedItemIndex = SendMessage(base.Handle, LVM_GETNEXTITEM, (IntPtr)(-1), (IntPtr)LVNI_SELECTED).ToInt32(); 
     if (selectedItemIndex > -1) 
     { 
      LVITEM lvi = new LVITEM(); 
      lvi.cchTextMax = 1024; 
      lvi.pszText = pStringBuffer; 
      Marshal.StructureToPtr(lvi, pItemBuffer, false); 
      int numChars = SendMessage(base.Handle, LVM_GETITEMTEXT, (IntPtr)selectedItemIndex, pItemBuffer).ToInt32(); 
      if (numChars > 0) 
      { 
       item = Marshal.PtrToStringUni(lvi.pszText, numChars); 
      } 
     } 

     Marshal.FreeHGlobal(pStringBuffer); 
     Marshal.FreeHGlobal(pItemBuffer); 

     return item; 
    } 

    struct LVITEM 
    { 
     public int mask; 
     public int iItem; 
     public int iSubItem; 
     public int state; 
     public int stateMask; 
     public IntPtr pszText; 
     public int cchTextMax; 
     public int iImage; 
     public IntPtr lParam; 
     public int iIndent; 
     public int iGroupId; 
     int cColumns; // tile view columns 
     public IntPtr puColumns; 
     public IntPtr piColFmt; 
     public int iGroup; 

    } 
} 

public class ExecuteEventArgs : EventArgs 
{ 
    public string SelectedItem { get; private set; } 
    public bool Cancel { get; set; } 

    internal ExecuteEventArgs(string selectedItem) 
    { 
     SelectedItem = selectedItem; 
    } 
} 

Esto debería darle una idea de lo que tendría que hacer. Si desea eventos más que simples, es posible que desee buscar un control alternativo, aunque por lo que he visto en las áreas gratuitas y de bajo costo, existen algunos controles bastante decentes, pero todos tienen algunos caprichos y no ofrecen un explorador perfecto. experiencia.

Recuerde que este código se compiló bastante rápido sin manejo de errores o comentarios e ignorando varios problemas, como múltiples elementos seleccionados, así que úselo como guía y bajo su propio riesgo.

+0

Felicitaciones por el esfuerzo en la respuesta, pero la respuesta aceptada debe ir a la solución que realmente utilicé. Entonces, si hubiera podido dividir la recompensa, lo hubiera hecho, pero en realidad estoy usando el proyecto citado en la otra respuesta ... – Unsliced

+0

Si funciona para usted, eso es genial. Sin embargo, si esto no es para uso personal, le recomendaría que use la solución LogicNP sugerida por uzbones. El elemento de proyecto de código es un esfuerzo valioso, pero está lejos de ser robusto por decir lo menos. –

3

LogicNP Software cuenta con dos controles (FileView y ShComboBox) que hacen lo que busca: http://www.ssware.com/fldrview.htm

Puede descargar una prueba de su página, sin embargo, es ~ 130 $ para la licencia.

1

Si está contento con Windows   Vista solamente y con un control COM, IExplorerBrowser podría ser aceptable para sus necesidades.

This The Code Project article muestra su uso dentro de un programa MFC pero at least one other person parece haberlo hecho funcionar en C# después de un esfuerzo.

La API más nueva expone considerablemente más programabilidad que la simple interceptación de mensajes, pero es (obviamente) inútil para las plataformas más antiguas.

1

Consulte este artículo here, que muestra cómo hacerlo en .NET y WinForms. Hacerlo de esta manera le da un control total sobre lo que el usuario ve.

Lo he usado en una de mis aplicaciones y funciona muy bien. Puede mostrar íconos/detalles/vista de lista e impide que el usuario se mueva a otros directorios (que a menudo es el problema de mostrar los diálogos de archivo/directorio estándar).

lo uso para mostrar la pantalla como la de abajo below http://img7.imageshack.us/img7/7647/screenshotbaf.png:

3

He escrito una biblioteca que podría ser capaz de ayudarle. Puede encontrarlo en: http://gong-shell.sourceforge.net/

El control que está buscando es el ShellView. También hay tutoriales sobre cómo crear un clon simple de Windows Explorer en solo unas pocas líneas.

Nota para usuarios de .NET 4.0: Gong-shell está actualmente roto para 4.0. El marco introdujo cambios en Interop y se compilará bien, pero causará problemas diferentes al interactuar con shell32 (notablemente la API apilada, lo que lleva a una desreferencia del puntero nulo no administrado).

+0

Lo he usado y he tenido algunos problemas. El mayor problema es que no puedo hacer doble clic en un archivo y hacer que inicie la aplicación predeterminada para el archivo. Si trato de cambiar el nombre de un archivo, la tecla de borrar no funcionará para eliminar caracteres del nombre de archivo existente. Tengo que usar el retroceso. Si escribo la letra 'i' mientras cambio el nombre, ¡las operaciones de cambio de nombre cesan! No he pasado mucho tiempo depurando, pero son problemas muy frustrantes. –

Cuestiones relacionadas