2009-02-12 102 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,导致非托管的空指针解引用)。

相关问题