2009-02-12 6 views
12

Meine Anwendung (C#, .NET 3.5) generiert Dateien und zusätzlich zu Ereignissen, auf die reagiert werden kann, möchte ich den Zielordner dem Benutzer anzeigen in einer Form. Die Dateiliste wird in derselben Form wie andere Informationen angezeigt.Einbetten einer Dateiexplorer-Instanz in ein Windows Forms-Anwendungsformular

Ich verwende eine Instanz der WebBrowser Kontrolle (System.Windows.Forms.WebBrowser), dann navigieren zu dem Ordner. Dies zeigt eine Standardansicht des Explorer-Fensters mit der Dateiübersicht auf der linken Seite und den Dateien in der Ansicht "Kacheln" (großes Symbol und Text).

Zum Beispiel

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

Ich mag würde die Platte zu unterdrücken und die Dateiliste anzuzeigen in der Detailansicht. Der Benutzer kann dazu über einen Rechtsklick, Kontextmenü, aber ich möchte es automatisch kommen.

Ich hätte lieber nicht mein eigenes TreeView, DataGridView oder was auch immer; Das WebBrowser-Steuerelement führt alle Aktualisierungen und Umsortierungen durch und nicht "kostenlos".

Gibt es einen besseren Weg? Ein anderes Steuerelement zu verwenden oder einige zusätzliche Argumente an das Steuerelement übergeben?

Und wenn ich Ereignisse einfangen könnte (zum Beispiel Dateien ausgewählt/umbenannt/doppelt geklickt, etc.), dann umso besser!

+0

Was ich nützlich fand, ist die (kommerzielle) [ShellBrowser-Komponente] (http://www.jam-software.com/shellbrowser_net/?language=EN). –

Antwort

4

Um die Umbenennung zu handhaben, zu löschen und andere Anpassung, die Sie benötigen, machen Sie Ihre eigene Datei-Explorer zu schreiben. WebBrowser-Steuerelement ist nicht für Ihre Bedürfnisse geeignet. Es ist nur ein Wrapper über ActiveX-Komponente.
Sie sollten this codeproject article überprüfen. Es enthält eine Implementierung von Datei-Explorer. Es gibt einige weitere Proben von Dateibrowser:
one
two

+0

Darum habe ich mir Sorgen gemacht - ich muss viel eigenen Code hinzufügen. Ich hoffte, dass der Web-Browser in der Lage sein könnte, Argumente zu übergeben, um meine faulen Knochen die Mühe zu sparen! – Unsliced

0

Wenn Sie ein anderes Fenster öffnen möchten, um den Inhalt des Zielordners anzuzeigen, können Sie System.Windows.Forms.OpenFileDialog oder SaveFileDialog verwenden oder von FileDialog erben und es erweitern.

Um dem Benutzer zu ermöglichen, einen Ordner auszuwählen, können Sie FolderBrowserDialog verwenden, obwohl ich als Benutzer dieses Steuerelement nicht mag.

Hilft dies oder Sie müssen unbedingt ein Steuerelement in Ihr Formular einbetten?

Asaf

+0

Ggg .. wie wirst du es in deine Form integrieren? Und die Option "Nicht aktiviert" möchte hier eine Liste generierter Dateien anzeigen, anstatt Dateien zu öffnen oder Zielordner auszuwählen. – zihotki

+0

Ich begann und beende mit der Frage, ob * es * in der gleichen Form sein muss. Aus der Frage ist es für mich nicht klar, ob es in eine Form eingebettet ist, weil es mit einem WebBrowser implementiert ist, oder weil die Dateiansicht neben etwas anderem steht. Es gibt keine direkte Möglichkeit, einen Dialog in ein Formular einzubetten. –

+0

Es muss eher in der gleichen Form sein, wie die Informationen neben anderen Fortschritts- und Statusaktualisierungen angezeigt werden müssen. Es ist überraschend, dass wir den Dialog zum Öffnen/Speichern von Dateien/Erstellen von Ordnern geöffnet haben, aber keinen, der explizit durchsucht werden soll ... – Unsliced

8

ACHTUNG: Lange Post mit viel Code.

Wenn Sie im Webbrowser-Steuerelement zu einem Dateisystemordner navigieren, enthält das Webbrowser-Steuerelement ein Shell-Ansichtsfenster, das wiederum die Explorer-Listenansicht enthält. In der Tat ist dies genau das Gleiche, was der Explorer-Prozess genauso macht wie die Dateidialoge und der Internet Explorer. Dieses Shell-Fenster ist kein Steuerelement, daher gibt es keine Methoden, die darauf aufgerufen werden können, oder Ereignisse, die abonniert werden können, aber es kann Windows-Nachrichten empfangen und es kann unterklassifiziert werden.

Es stellt sich heraus, dass der Teil Ihrer Frage, der sich auf das automatische Einstellen der Ansicht auf Details bezieht, tatsächlich ziemlich einfach ist. Suchen Sie im Navigated-Ereignis Ihres Webbrowser-Steuerelements einfach das Handle für das Shell-Ansichtsfenster und senden Sie ihm eine WM_COMMAND-Nachricht mit einer bestimmten Shell-Konstante (SHVIEW_REPORT). Dies ist ein nicht dokumentierter Befehl, der jedoch auf allen Windows-Plattformen bis einschließlich Windows 2008 unterstützt wird und fast sicher unter Windows 7 ausgeführt wird.Einige Code hinzufügen zeigen, um Ihr Web-Browser-Form folgt aus:

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

Jedes Mal, wenn der Web-Browser auf ein neues Fenster navigiert (einschließlich, wenn ein Ordner aus der Explorer-Ansicht geöffnet ist) ein neues Shell Sichtfenster so wird erstellt Die Nachricht muss in jedem navigierten Ereignis erneut an das neue Fenster gesendet werden.

Für den zweiten Teil Ihrer Frage möchten Sie Ereignisse aus der Explorer-Listenansicht erhalten. Das ist ein bisschen schwieriger als der erste Teil. Um dies zu tun, müssten Sie das Listenansichtsfenster unterteilen und dann die Windows-Nachrichten auf diejenigen überwachen, die Sie interessieren (wie WM_LBUTTONDBLCLK). Um ein Fenster unterzuordnen, müssen Sie eine eigene Klasse erstellen, die von der NativeWindow-Klasse abgeleitet ist, und ihr das Handle des Fensters zuweisen, das Sie überwachen müssen. Sie können dann die Fensterprozedur überschreiben und die verschiedenen Nachrichten wie gewünscht bearbeiten. Im Folgenden finden Sie ein Beispiel für das Erstellen eines Doppelklickereignisses - es ist relativ einfach, aber um umfassenden Zugriff auf die Explorer-Listenansicht zu erhalten, kann viel mehr Arbeit erforderlich sein, als Sie bereit sind.

Fügen Sie dieses Formular:

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

und diese beiden Zeilen in die navigierte Event-Handler (direkt nach dem Sendmessage):

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

Dann fügen Sie die folgenden Klassen:

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

Dies sollte Ihnen eine Vorstellung davon geben, was Sie tun müssten. Wenn Sie mehr als nur einfache Ereignisse möchten, sollten Sie nach einer alternativen Steuerung suchen, obwohl es nach dem, was ich in den kostenlosen und kostengünstigen Bereichen gesehen habe, einige recht ordentliche Kontrollen gibt, aber alle haben ein paar Macken und keinen nahtlosen Explorer Erfahrung.

Denken Sie daran, dass dieser Code ziemlich schnell ohne Fehlerbehandlung oder Kommentare zusammengestellt wurde und mehrere Probleme wie mehrere ausgewählte Elemente ignoriert. Verwenden Sie ihn daher als Richtlinie und auf eigenes Risiko.

+0

Kudos für die Mühe in der Antwort, aber die akzeptierte Antwort sollte zu der Lösung gehen, die ich tatsächlich verwendete. Also wenn ich das Kopfgeld geteilt hätte, hätte ich es getan, aber ich benutze das Projekt, das in der anderen Antwort zitiert wird ... – Unsliced

+0

Wenn es für dich funktioniert, ist das großartig. Wenn dies jedoch nicht für den persönlichen Gebrauch gedacht ist, empfehle ich Ihnen dringend, die von UZBones vorgeschlagene LogicNP-Lösung zu verwenden. Das Code-Projektelement ist eine mutige Anstrengung, aber es ist weit davon entfernt, robust zu sein, um es gelinde auszudrücken. –

3

LogicNP Software verfügt über zwei Kontrollen (Fileview und ShComboBox), das zu tun, was Sie suchen: http://www.ssware.com/fldrview.htm

Sie einen Versuch von ihrer Seite herunterladen können, aber es ist ~ 130 für die Lizenz $.

1

Wenn Sie glücklich sein Windows   Vista nur und ein COM Steuerelement, IExplorerBrowser möglicherweise für Sie akzeptabel ist.

This The Code Project article zeigt seine Verwendung in einem MFC Programm, aber at least one other person scheint es nach einiger Anstrengung in C# zu arbeiten.

Die neuere API bietet deutlich mehr Programmierbarkeit als das Abfangen von Nachrichten, ist aber (offensichtlich) nutzlos für ältere Plattformen.

1

Schauen Sie sich diesen Artikel here an, es zeigt, wie man das in .NET und WinForms macht. Auf diese Weise erhalten Sie die volle Kontrolle darüber, was der Benutzer sieht.

Ich habe es in einer meiner Anwendungen verwendet und es funktioniert wirklich gut. Sie können die Symbol-/Detail-/Listenansicht anzeigen und den Benutzer daran hindern, in andere Verzeichnisse zu wechseln (was oft das Problem ist, die Standard-Datei/Verzeichnis-Dialoge anzuzeigen.

ich es verwenden, um den Bildschirm wie unten below http://img7.imageshack.us/img7/7647/screenshotbaf.png zeigen:

3

ich eine Bibliothek geschrieben haben, dass Sie helfen könnten fähig sein. Sie finden es unter: http://gong-shell.sourceforge.net/

Die Kontrolle, die Sie suchen, ist das ShellView. Dort finden Sie Tutorials, wie Sie einen einfachen Windows Explorer-Klon in nur wenigen Zeilen erstellen können.

Hinweis für .NET 4.0-Benutzer: Gong-Shell ist derzeit für 4,0 gebrochen. Das Framework führte Änderungen in Interop ein und es wird gut funktionieren, aber verschiedene Probleme verursachen, wenn es mit der Shell32 (insbesondere der Shellicon API, was zu einer nicht verwalteten Nullzeigerdereferenzierung führt).

+0

Ich habe das benutzt und ich hatte einige Probleme. Das größte Problem ist, dass ich nicht auf eine Datei doppelklicken kann und sie die Standardanwendung für die Datei starten lassen. Wenn ich versuche, eine Datei umzubenennen, funktioniert die Löschtaste nicht, um Zeichen des vorhandenen Dateinamens zu löschen. Ich muss die Rücktaste verwenden. Wenn ich beim Umbenennen den Buchstaben "i" tippe, werden die Umbenennungsvorgänge beendet! Ich habe nicht viel Zeit Debuggen verbracht, aber sie sind sehr frustrierende Probleme. –