2009-02-12 6 views
12

Mon application (C#, .NET 3.5) génère des fichiers et, en plus d'élever des événements qui peuvent être interceptés et réagis, je souhaite afficher le dossier cible à l'utilisateur sous une forme. La liste de fichiers est affichée sous la même forme que d'autres informations.Incorporation d'une instance de File Explorer dans un formulaire d'application Windows Forms

J'utilise une instance du contrôle WebBrowser (System.Windows.Forms.WebBrowser), puis naviguez vers le dossier. Cela affiche une vue par défaut de la fenêtre de l'explorateur, avec le panneau de résumé du fichier sur la gauche et les fichiers dans la vue «Mosaïques» (grande icône et texte).

Par exemple,

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

Je voudrais supprimer le panneau et pour afficher la liste des fichiers dans la vue détaillée. L'utilisateur peut y accéder via un menu contextuel, mais je voudrais qu'il apparaisse automatiquement. Je préférerais ne pas avoir à créer mon propre TreeView, DataGridView ou quoi que ce soit; le contrôle WebBrowser fait toute la mise à jour et re-trier et autres 'gratuitement'.

Y a-t-il un meilleur moyen? Un contrôle différent à utiliser ou des arguments supplémentaires à transmettre au contrôle?

Et si je pouvais piéger des événements (par exemple, les fichiers étant sélectionnés/renommés/double-cliqué, etc.) alors tant mieux!

+0

Ce que j'ai trouvé utile est le composant (commercial) [ShellBrowser] (http://www.jam-software.com/shellbrowser_net/?language=EN). –

Répondre

4

Afin de gérer renommer, supprimer et faire autre personnalisation vous devez écrire votre propre explorateur de fichiers. Le contrôle WebBrowser ne convient pas à vos besoins. C'est juste une enveloppe sur le composant ActiveX. Vous devez vérifier this codeproject article. Il contient une implémentation de l'explorateur de fichiers. Il y a quelques échantillons de navigateur de fichiers:
one
two

+0

C'est ce dont je m'inquiétais - devoir ajouter beaucoup de mon propre code. J'espérais que le navigateur web pourrait avoir des arguments passés pour sauver mes os paresseux! – Unsliced

0

Si vous souhaitez ouvrir une fenêtre différente pour afficher le contenu du dossier cible, vous pouvez utiliser System.Windows.Forms.OpenFileDialog ou SaveFileDialog, ou hériter de FileDialog et l'étendre.

Pour permettre à l'utilisateur de sélectionner un dossier, vous pouvez utiliser FolderBrowserDialog, mais en tant qu'utilisateur, je n'aime pas ce contrôle.

Est-ce que cela vous aide ou vous devez absolument intégrer un contrôle dans votre formulaire?

Asaf

+0

Ggg .. comment allez-vous l'intégrer dans votre formulaire? Et Unsliced ​​veut ici afficher une liste de fichiers générés au lieu d'ouvrir des fichiers ou de sélectionner un dossier cible. – zihotki

+0

J'ai commencé et terminé en demandant si * doit * être sous la même forme. De la question il n'est pas clair pour moi si elle est incorporée dans un formulaire * parce que * il est implémenté avec un WebBrowser, ou parce que la vue de fichier est à côté d'autre chose. Il n'y a pas de moyen simple d'incorporer une boîte de dialogue dans un formulaire. –

+0

Il doit plutôt être dans la même forme que l'information doit être montré avec d'autres progrès et mises à jour de statut. Il est surprenant que nous ayons une boîte de dialogue ouvrir/enregistrer un fichier/créer des dossiers, mais aucune pour naviguer explicitement ... – Unsliced

8

AVERTISSEMENT: après long avec beaucoup de code.

Lorsque vous déplacez le contrôle du navigateur Web vers un dossier du système de fichiers, le contrôle du navigateur Web héberge une fenêtre de vue du shell qui à son tour héberge la vue de la liste des explorateurs. En fait, c'est exactement la même chose que le processus Explorer, ainsi que les boîtes de dialogue de fichiers et Internet Explorer. Cette fenêtre shell n'est pas un contrôle donc il n'y a pas de méthodes qui peuvent être appelées ou d'événements auxquels on peut s'abonner mais elle peut recevoir des messages Windows et elle peut être sous-classée.

Il s'avère que la partie de votre question traitant de la configuration automatique de la vue est en fait assez facile. Dans l'événement Navigated de votre navigateur Web, recherchez simplement le handle dans la fenêtre de vue du shell et envoyez-lui un message WM_COMMAND avec une constante de shell particulière (SHVIEW_REPORT). Il s'agit d'une commande non documentée, mais elle est prise en charge sur toutes les plates-formes Windows jusqu'à et y compris Windows 2008 et sera presque certainement sur Windows 7.Une partie du code à ajouter à votre formulaire de navigateur Web démontre ceci:

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

Chaque fois que le navigateur Web accède à une nouvelle fenêtre (y compris quand un dossier est ouvert depuis l'explorateur) une nouvelle fenêtre de vue shell est créé afin le message doit être renvoyé à la nouvelle fenêtre dans chaque événement Navigated.

Pour la deuxième partie de votre question, vous aimeriez recevoir des événements de la liste de l'explorateur. C'est un peu plus difficile que la première partie. Pour ce faire, vous devez sous-classer la fenêtre d'affichage de la liste, puis surveiller les messages Windows pour ceux qui vous intéressent (tels que WM_LBUTTONDBLCLK). Afin de sous-classer une fenêtre, vous devez créer votre propre classe dérivée de la classe NativeWindow et lui assigner le handle de la fenêtre que vous devez surveiller. Vous pouvez ensuite remplacer sa procédure Window et gérer les différents messages comme vous le souhaitez. Vous trouverez ci-dessous un exemple de création d'un événement à double clic: il est relativement simple, mais pour accéder à la vue de la liste d'explorateurs, il faut beaucoup plus de travail que ce que vous êtes prêt à faire.

Ajoutez ceci à votre formulaire:

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

et ces deux lignes au gestionnaire d'événements Navigated (juste après le SendMessage):

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

ajouter ensuite les classes suivantes:

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

Cela devrait vous donner une idée de ce que vous devez faire. Si vous voulez plus que des événements assez simples, vous voudrez peut-être chercher un contrôle alternatif, mais d'après ce que j'ai vu dans les zones gratuites et peu coûteuses, il y a des contrôles assez corrects mais ils ont tous quelques bizarreries expérience. Rappelez-vous que ce code a été mis en place assez rapidement sans erreur de gestion ou de commentaires et en ignorant plusieurs problèmes tels que plusieurs éléments sélectionnés, alors utilisez-le comme un guide et à vos risques et périls.

+0

Bravo pour l'effort dans la réponse, mais la réponse acceptée devrait aller à la solution que j'ai effectivement utilisée. Donc, si j'avais pu partager la prime que j'aurais fait, mais je suis en train d'utiliser le projet cité dans l'autre réponse ... – Unsliced

+0

Si cela fonctionne pour vous, c'est génial. Cependant, si ce n'est pas pour un usage personnel, je vous recommande fortement d'utiliser la solution LogicNP proposée par uzbones. L'élément de projet de code est un effort vaillant, mais il est loin d'être robuste, c'est le moins qu'on puisse dire. –

3

LogicNP Software dispose de deux commandes (fileview et ShComboBox) qui font ce que vous cherchez: http://www.ssware.com/fldrview.htm

Vous pouvez télécharger un essai de leur page, mais il est ~ 130 $ pour la licence.

1

Si vous êtes heureux d'être Windows   Vista seulement et enveloppant un contrôle COM, IExplorerBrowser pourrait être acceptable pour vos besoins.

This The Code Project article montre son utilisation dans un programme MFC mais at least one other person semble avoir réussi à travailler en C# après quelques efforts.

L'API plus récente expose considérablement plus de programmabilité que l'interception simple des messages, mais elle est (évidemment) inutile pour les plates-formes plus anciennes.

1

Consultez cet article here, il montre comment faire cela dans .NET et WinForms. Le faire de cette façon donne un contrôle total sur ce que l'utilisateur voit. Je l'ai utilisé dans l'une de mes applications et ça fonctionne très bien. Vous pouvez afficher l'affichage des icônes/détails/liste et cela empêche l'utilisateur de se déplacer vers d'autres répertoires (ce qui pose souvent le problème de l'affichage des boîtes de dialogue de fichier/répertoire standard.

Je l'utilise pour afficher l'écran comme celui ci-dessous below http://img7.imageshack.us/img7/7647/screenshotbaf.png:

3

J'ai écrit une bibliothèque qui pourrait être en mesure de vous aider. Vous pouvez le trouver à: http://gong-shell.sourceforge.net/

Le contrôle que vous cherchez est le ShellView. Il y a des tutoriels sur la façon de créer un simple clone Windows Explorer en seulement quelques lignes.

Note pour les utilisateurs de .NET 4.0: Gong-shell est actuellement cassé pour 4.0. Le framework introduit des changements dans Interop et il se construira très bien mais causera des problèmes différents lors de l'interfaçage avec shell32 (notamment l'api shellicon, conduisant à un déréférencement de pointeur null non géré).

+0

Je l'ai utilisé et j'ai eu quelques problèmes. Le plus gros problème est que je ne peux pas double-cliquer sur un fichier et le faire démarrer l'application par défaut pour le fichier. Si j'essaie de renommer un fichier, la touche de suppression ne fonctionnera pas pour supprimer les caractères du nom de fichier existant. Je dois utiliser le retour arrière. Si je tape la lettre 'i' en renommant, les opérations de renommage cessent! Je n'ai pas passé beaucoup de temps à déboguer, mais ce sont des problèmes très frustrants. –

Questions connexes