在 Windows 窗体应用程序表单中嵌入文件资源管理器实例

2024-02-27

我的(C#、.NET 3.5)应用程序生成文件,除了引发可以捕获和响应的事件之外,我还想以表单向用户显示目标文件夹。文件列表与其他信息以相同的形式显示。

我正在使用一个实例WebBrowser控制 (System.Windows.Forms.WebBrowser),然后导航到该文件夹​​。这显示了资源管理器窗口的一些默认视图,文件摘要面板位于左侧,文件位于“平铺”(大图标和文本)视图中。

例如,

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

我想隐藏该面板并在“详细信息”视图中查看文件列表。用户可以通过右键单击上下文菜单来访问此内容,但我希望它自动出现。

我不想构建自己的 TreeView、DataGridView 或其他什么; WebBrowser 控件“免费”执行所有更新和重新排序等操作。

有没有更好的办法?使用不同的控件或传递给控件的一些附加参数?

如果我可以捕获事件(例如,文件被选择/重命名/双击等),那就更好了!


警告:带有大量代码的长帖子。

当您将 Web 浏览器控件导航到文件系统文件夹时,Web 浏览器控件会托管一个 shell 视图窗口,该窗口又托管资源管理器列表视图。事实上,这与 Explorer 进程以及文件对话框和 Internet Explorer 所做的事情完全相同。该 shell 窗口不是一个控件,因此没有可以在其上调用的方法或可以订阅的事件,但它可以接收 Windows 消息并且可以对其进行子类化。

事实证明,您的问题中涉及自动将视图设置为“详细信息”的部分实际上非常简单。在 Web 浏览器控件的 Navigated 事件中,只需找到 shell 视图窗口的句柄并向其发送带有特定 shell 常量 (SHVIEW_REPORT) 的 WM_COMMAND 消息。这是一个未记录的命令,但它在 Windows 2008 及之前的所有 Windows 平台上都受支持,并且几乎肯定会在 Windows 7 上受到支持。添加到 Web 浏览器表单中的一些代码演示了这一点:

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

每次 Web 浏览器导航到新窗口(包括从资源管理器视图中打开文件夹时),都会创建一个新的 shell 视图窗口,因此必须在每个导航事件中将消息重新发送到新窗口。

对于问题的第二部分,您希望从资源管理器列表视图接收事件。这比第一部分要困难一些。为此,您需要对列表视图窗口进行子类化,然后监视您感兴趣的窗口消息(例如 WM_LBUTTONDBLCLK)。为了对窗口进行子类化,您需要创建自己的从 NativeWindow 类派生的类,并为其分配需要监视的窗口的句柄。然后您可以重写其窗口过程并根据需要处理各种消息。下面是创建双击事件的示例 - 它相对简单,但要广泛访问资源管理器列表视图可能需要做比您愿意做的更多的工作。

将其添加到您的表单中:

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

并将这两行添加到 Navigated 事件处理程序(紧接在 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;
    }
}

这应该会让您了解您需要做什么。如果您想要的不仅仅是相当简单的事件,您可能需要寻找替代控件,尽管从我在免费和低成本区域看到的情况来看,有一些相当不错的控件,但它们都有一些怪癖,并且不会提供无缝的浏览器经验。

请记住,此代码是相当快地组合在一起的,没有错误处理或注释,并且忽略了多个问题(例如多个选定的项目),因此将其用作指南并自行承担风险。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Windows 窗体应用程序表单中嵌入文件资源管理器实例 的相关文章

  • LINQ to XML - 如何正确使用 XDocument

    现在我首先要说的是 这确实是一项任务 然而 在我遇到 Linq to XML 语法之前 我几乎已经完成了它 我有 2 个课程 曲目和 CD 现在作为作业的一部分 我创建了一张 CD 然后向其中添加了一些曲目 在搜索了大量完美解释了如何从 x
  • .NET 可移植类库中的 .ToShortDateString 发生了什么

    我想知道为什么没有 ToShortDateString在 NET 可移植类库中 我有 2 个项目 Silverlight 和常规 NET 类库 使用相同的代码 并且代码涉及调用 ToShortDateString on a DateTime
  • 当我单击 GridView 项时返回 ImageView 实例

    当我点击GridView项时如何返回ImageView实例 我为 ItemClick 创建自定义绑定事件 public class ItemClickSquareBinding MvxBaseAndroidTargetBinding pri
  • 使用 VSTO 更改 Outlook 设置

    我刚刚花了大约 4 个小时试图弄清楚如何以编程方式检索 设置 Microsoft Outlook 2010 的 Outlook 设置 我所说的 设置 是指文件 选项 邮件下的设置 我想做的是检索用户设置的设置列表 自动化我们每天需要在某些消
  • C++ 在 Vector 中使用不可分配的对象

    我想将对象列表存储在std vector 但对象包含引用且无法分配给 但是 我可以复制构造该对象 我能想到的唯一选择是使用指针来包装对象并在需要分配指针时重新设置指针 但这样做的语法会显着降低可读性 特别是在使用迭代器时 我更喜欢另一种选择
  • Qt中正确的线程方式

    我的图像加载非常耗时 图像很大 并且在加载时也完成了一些操作 我不想阻止应用程序 GUI 我的想法是在另一个线程中加载图像 发出图像已加载的信号 然后用该图像重绘视图 我的做法 void Window loadImage ImageLoad
  • 推送 Lua 表

    我已经创建了一个Lua表C 但我不知道如何将该表推入堆栈顶部 以便我可以将其传递给 Lua 函数 有谁知道如何做到这一点 这是我当前的代码 lua createtable state libraries size 0 int table i
  • 当格式字符串包含“{”时,String.Format 异常

    我正在使用 VSTS 2008 C Net 2 0 执行以下语句时 String Format 语句抛出 FormatException 有什么想法是错误的吗 这是获取我正在使用的 template html 的位置 我想在 templat
  • 为什么以下代码不允许我使用 fgets 获取用户输入但可以使用 scanf?

    这是一个更大程序的简短摘录 但该程序的其余部分无关紧要 因为我认为我能够隔离该问题 我怀疑这与我使用 fgets 的方式有关 我读过 最好使用 fgets 而不是 scanf 但我似乎无法让它在这里正常工作 当我使用以下代码时 程序不会给我
  • 如何在 C++ 中使用 PI 常数

    我想在一些 C 程序中使用 PI 常数和三角函数 我得到三角函数include
  • 如何使用 Clang 查找内存泄漏

    我在我的机器 ubuntu 中安装了 Clang 以便发现我的 C 代码中的内存泄漏 我编写了一个示例代码来检查它的工作情况 如下所示 File hello c for leak detection include
  • 用 C# 编写的带有点击移动的 WPF 游戏

    我试图将标签网格移动到鼠标的位置 就像冒险游戏中的移动一样 理想情况下 我会在途中删除并重新绘制它们 但是 现在我只想弄清楚如何将 int 转换为厚度或 pointtoscreen 到目前为止我有 player XMove int Mous
  • 通过 MSBuild 调用 cl.exe 时无限期挂起

    我正在尝试在我的 主要是 C 项目上运行 MSBuild 想象一下一个非常庞大的代码库 Visual Studio 2015 是有问题的工具集 Windows 7 SP1 和 VS 2015 更新 2 即使使用 m 1 从而迫使它仅使用一个
  • 改进C++逐行读取文件的能力?

    我正在解析大约 500GB 的日志文件 我的 C 版本需要 3 5 分钟 我的 Go 版本需要 1 2 分钟 我正在使用 C 的流来流式传输文件的每一行以进行解析 include
  • 在 MVVM 中,可以在视图后面的代码中访问 ViewModel 吗?

    在 MVVM 模式中 是否可以接受甚至可以访问视图代码后面的 ViewModel 属性 我有一个可观察的集合 它填充在 ViewModel 中 我需要在视图中使用它来绑定到带有链接列表的无限滚动条 IE private LinkedList
  • SSBO 是更大的 UBO?

    我目前正在 OpenGL 4 3 中使用 UBO 进行渲染 以将所有常量数据存储在 GPU 上 诸如材料描述 矩阵等内容 它可以工作 但是 UBO 的小尺寸 我的实现为 64kB 迫使我多次切换缓冲区 减慢渲染速度 我正在寻找类似的方法来存
  • C# - 为什么我需要初始化 [Out] 参数

    我有几个从本机 dll 导入的方法 使用以下语法 internal static class DllClass DllImport Example dll EntryPoint ExampleFunction public static e
  • 如何使用 g++ 在 c++ 20 中使用模块?

    我读了这个链接https gcc gnu org wiki cxx modules https gcc gnu org wiki cxx modules并尝试从该网站复制以下示例 我已经知道这个编译器部分支持模块系统 注 我用的是windo
  • 局部静态变量初始化是线程安全的[重复]

    这个问题在这里已经有答案了 假设我有一个包含三个静态函数的类 如下所示 include
  • C++0x 中的新 unicode 字符

    我正在构建一个 API 它允许我获取各种编码的字符串 包括 utf8 utf16 utf32 和 wchar t 根据操作系统 可能是 utf32 或 utf16 新的 C 标准引入了新类型char16 t and char32 t没有这么

随机推荐