2009-02-12 70 views
12

我的(C#,.NET 3.5)應用程序生成文件,除了引發可以捕獲和響應的事件外,我還想將目標文件夾顯示給用戶以一種形式。文件列表以與其他信息相同的形式顯示。將文件資源管理器實例嵌入到Windows窗體應用程序窗體中

我正在使用WebBrowser控件的實例(System.Windows.Forms.WebBrowser),然後導航到該文件夾​​。這會顯示資源管理器窗口的一些默認視圖,左側的文件摘要面板和「Tiles」(大圖標和文本)視圖中的文件。

例如,

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

我想打壓面板和詳細信息視圖中查看文件列表。用戶可以通過右鍵單擊上下文菜單來獲得此信息,但我希望它自動出現。

我寧願不必建立自己的TreeView,DataGridView或其他; WebBrowser控件執行所有更新和重新排序,以及免費的任何「更新」。

有沒有更好的方法?一個不同的控件使用或一些額外的參數傳遞給控件?

如果我可以捕捉事件(例如,文件被選中/重命名/雙擊等),那麼一切都會更好!

+0

我發現有用的是(商業)[ShellBrowser組件](http://www.jam-software.com/shellbrowser_net/?language=EN)。 – 2013-11-11 12:44:15

回答

4

爲了處理重命名,刪除,讓你需要編寫自己的文件瀏覽器等定製。 WebBrowser控件不適合您的需求。它只是一個ActiveX組件的封裝。
您應該檢查this codeproject article。它包含一個文件瀏覽器的實現。很少有文件瀏覽器的更多樣本:
one
two

+0

這就是我所擔心的 - 需要添加我自己的代碼。我希望網絡瀏覽器能夠通過傳遞參數來保存我的懶骨頭! – Unsliced 2009-02-23 08:38:58

0

如果您想打開不同的窗口來顯示目標文件夾的內容,您可以使用System.Windows.Forms.OpenFileDialog或SaveFileDialog,或從FileDialog繼承並擴展它。

要允許用戶選擇文件夾,您可以使用FolderBrowserDialog,但作爲用戶我不喜歡該控件。

這是否有幫助,或者您絕對需要在窗體中嵌入控件?

阿薩夫

+0

Ggg ..你如何將它整合到你的表單中?而Unsliced希望在這裏顯示生成文件的列表,而不是打開文件或選擇目標文件夾。 – zihotki 2009-02-21 10:09:47

+0

我開始和結束時詢問它是否*具有相同的形式。從這個問題我不清楚它是嵌入在表單中*是因爲它是用WebBrowser實現的,還是因爲文件視圖與其他內容一起。沒有直接的方式來嵌入窗體中的對話框。 – 2009-02-21 10:22:52

+0

它必須採用相同的形式,因爲信息需要與其他進度和狀態更新一起顯示。令人驚訝的是,我們打開/保存文件/創建文件夾對話框,但沒有明確瀏覽... – Unsliced 2009-02-23 08:40:14

8

警告:長時間使用後大量的代碼。

將Web瀏覽器控件導航到文件系統文件夾時,Web瀏覽器控件託管一個shell視圖窗口,該窗口依次託管資源管理器列表視圖。事實上,這與Explorer進程以及文件對話框和Internet Explorer完全相同。這個shell窗口不是一個控件,所以沒有可以對它調用的方法或者可以訂閱的事件,但它可以接收windows消息並且可以被分類。

事實證明,問題的一部分處理將視圖自動設置爲「細節」實際上非常簡單。在您的Web瀏覽器控件的Navigated事件中,只需找到shell視圖窗口的句柄,並使用特定的shell常量(SHVIEW_REPORT)向其發送WM_COMMAND消息。這是一個未公開的命令,但它在Windows 2008之前的所有Windows平臺上都受支持,幾乎肯定會在Windows 7上支持。某些代碼添加到您的網頁瀏覽器的形式說明了這一點:

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

每次網頁瀏覽器導航到一個新窗口中創建一個新的外殼視圖窗口,以便(當文件夾從資源管理器視圖中打開包含)該消息必須重新發送到每個Navigated事件中的新窗口。

對於問題的第二部分,您希望從資源管理器列表視圖中接收事件。這比第一部分要困難得多。要做到這一點,你需要對列表視圖窗口進行分類,然後監視你感興趣的窗口消息(如WM_LBUTTONDBLCLK)。爲了對窗口進行子類化,您需要創建自己的從NativeWindow類派生的類併爲其分配需要監視的窗口的句柄。然後,您可以覆蓋其Window程序並根據需要處理各種消息。下面是一個創建雙擊事件的例子 - 它比較簡單,但要獲得對Explorer列表視圖的廣泛訪問,可能需要做的工作比您願意做的要多得多。

添加到您的形式:

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

和這兩條線來導航的事件處理程序(該SendMessage函數之後):

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

然後添加以下類別:

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

這應該讓你知道你需要做什麼。如果你想要的不僅僅是相當簡單的事件,你可能想要尋找一種替代控制,儘管從我在免費和低成本領域看到的有一些相當不錯的控制,但它們都有一些怪癖,並且不會給無縫探測器經驗。

請記住,此代碼相當快,沒有錯誤處理或評論,並忽略了多個問題,如多個選定的項目,所以用它作爲指導,風險自負。

1

看看這篇文章here,它展示瞭如何在.NET和WinForms中做到這一點。這樣做可以完全控制用戶看到的內容。

我已經在我的一個應用程序中使用它,它工作得很好。您可以顯示icon/details/list視圖,並停止用戶移動到其他目錄(這通常是顯示標準文件/目錄對話框的問題。

我用它來顯示像一個下面below http://img7.imageshack.us/img7/7647/screenshotbaf.png屏幕:

3

我寫了也許能幫助你的庫。你可以在:http://gong-shell.sourceforge.net/

你正在尋找的控件是ShellView。有關於如何在幾行內創建一個簡單的Windows資源管理器克隆的教程。

.NET 4.0用戶注意事項:Gong-shell目前損壞爲4.0。該框架在Interop中引入了更改,它將構建得很好,但在與shell32接口時會引發不同的問題(特別是shellicon API,導致非託管的空指針解引用)。

相關問題