C# 在 Windows 窗体应用程序窗体中嵌入文件资源管理器实例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/542378/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Embedding a File Explorer instance in a Windows Forms application form
提问by Unsliced
My (C#, .NET 3.5) application generates files and, in addition to raising events that can be caught and reacted to, I want to display the target folder to the user in a form. The file-list is being shown within the same form as other information.
我的(C#、.NET 3.5)应用程序生成文件,除了引发可以捕获和响应的事件之外,我还想以表单形式向用户显示目标文件夹。文件列表以与其他信息相同的形式显示。
I'm using an instance of the WebBrowser
control (System.Windows.Forms.WebBrowser
), then navigating to the folder. This shows some default view of the explorer window, with the file summary panel on the left and the files in the 'Tiles' (large icon and text) view.
我正在使用WebBrowser
控件 ( System.Windows.Forms.WebBrowser
) 的一个实例,然后导航到该文件夹。这显示了资源管理器窗口的一些默认视图,左侧是文件摘要面板,文件位于“瓷砖”(大图标和文本)视图中。
For example,
例如,
wb.Navigate(@"c:\path\to\folder\");
I'd like to suppress the panel and to view the file list in the Details view. The user can get to this via a right-click, context menu, but I'd like it to come up automatically.
我想取消面板并在详细信息视图中查看文件列表。用户可以通过右键单击上下文菜单来访问它,但我希望它自动出现。
I'd rather not have to build my own TreeView, DataGridView or whatever; the WebBrowser control does all the updating and re-sorting and whatnot 'for free'.
我宁愿不必构建自己的 TreeView、DataGridView 或其他任何东西;WebBrowser 控件执行所有更新和重新排序以及诸如此类的“免费”操作。
Is there a better way? A different control to use or some additional arguments to pass to the control?
有没有更好的办法?使用不同的控件或传递给控件的一些附加参数?
And if I could trap events (for example, files being selected/renamed/double-clicked, etc.) then all the better!
如果我可以捕获事件(例如,文件被选择/重命名/双击等),那就更好了!
采纳答案by zihotki
In order to handle renaming, deleting and make other customization you need to write your own file explorer. WebBrowser control is not suitable for your needs. It's just a wrapper over ActiveX component.
You should check this codeproject article. It contains an implementation of file explorer. There are few more samples of file browser:
one
two
为了处理重命名、删除和进行其他自定义,您需要编写自己的文件浏览器。WebBrowser 控件不适合您的需要。它只是 ActiveX 组件的包装器。
您应该查看此 codeproject 文章。它包含文件资源管理器的实现。有文件浏览器的几个样本:
一个
2
回答by Asaf R
If you want to open a different window to display the target folder's content you can use System.Windows.Forms.OpenFileDialog, or SaveFileDialog, or inherit from FileDialog and extend it.
如果您想打开一个不同的窗口来显示目标文件夹的内容,您可以使用 System.Windows.Forms.OpenFileDialog 或 SaveFileDialog,或者从 FileDialog 继承并扩展它。
To allow the user to select a folder you can use FolderBrowserDialog, though as a user I don't like that control.
要允许用户选择文件夹,您可以使用 FolderBrowserDialog,但作为用户我不喜欢该控件。
Does this help or you absolutely have to embed a control in your form?
这是否有帮助,或者您绝对必须在表单中嵌入控件?
Asaf
阿萨夫
回答by Stephen Martin
WARNING: Long post with lots of code.
警告:带有大量代码的长篇文章。
When you navigate the web browser control to a file system folder the web browser control hosts a shell view window that in turn hosts the explorer list view. In fact this is exactly the same thing that the Explorer process does as well as the file dialogs and Internet Explorer. This shell window is not a control so there are no methods that can be called on it or events that can be subscribed to but it can receive windows messages and it can be sub-classed.
当您将 Web 浏览器控件导航到文件系统文件夹时,Web 浏览器控件托管一个 shell 视图窗口,该窗口又托管资源管理器列表视图。事实上,这与 Explorer 进程以及文件对话框和 Internet Explorer 完全相同。这个shell窗口不是一个控件,所以没有可以调用它的方法或可以订阅的事件,但它可以接收windows消息并且可以被子类化。
It turns out that the part of your question dealing with setting the view to Details automatically is actually quite easy. In your web browser control's Navigated event simply find the handle to the shell view window and send it a WM_COMMAND message with a particular shell constant (SHVIEW_REPORT). This is an undocumented command but it is supported on all Windows platforms up to and including Windows 2008 and almost certainly will be on Windows 7. Some code to add to your web browser's form demonstrates this:
事实证明,您的问题中处理自动将视图设置为 Details 的部分实际上非常简单。在您的 Web 浏览器控件的 Navigated 事件中,只需找到 shell 视图窗口的句柄并向其发送带有特定 shell 常量 (SHVIEW_REPORT) 的 WM_COMMAND 消息。这是一个未公开的命令,但它在所有 Windows 平台上都受支持,直到并包括 Windows 2008,并且几乎肯定会在 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;
}
Every time the web browser navigates to a new window (including when a folder is opened from within the explorer view) a new shell view window is created so the message must be re-sent to the new window in every Navigated event.
每次 Web 浏览器导航到新窗口时(包括从资源管理器视图中打开文件夹时),都会创建一个新的 shell 视图窗口,因此必须在每个 Navigated 事件中将消息重新发送到新窗口。
For the second part of your question you would like to receive events from the explorer list view. This is quite a bit more difficult than the first part. To do this you would need to sub-class the list view window and then monitor the windows messages for ones that interest you (such as WM_LBUTTONDBLCLK). In order to sub-class a window you would need to create your own class derived from the NativeWindow class and assign it the handle of the window that you need to monitor. You can then override its Window procedure and handle the various messages as you wish. Below is an example of creating a double click event - it is relatively simple but to get extensive access to the explorer list view may involve a lot more work than you are willing to do.
对于问题的第二部分,您希望从资源管理器列表视图接收事件。这比第一部分要困难得多。为此,您需要对列表视图窗口进行子类化,然后监视您感兴趣的窗口消息(例如 WM_LBUTTONDBLCLK)。为了对窗口进行子类化,您需要创建自己的从 NativeWindow 类派生的类,并将需要监视的窗口的句柄分配给它。然后,您可以覆盖其 Window 过程并根据需要处理各种消息。下面是一个创建双击事件的示例 - 它相对简单,但要获得对资源管理器列表视图的广泛访问可能涉及比您愿意做的更多的工作。
Add this to your form:
将此添加到您的表单中:
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);
}
and these two lines to the Navigated event handler (right after the SendMessage):
这两行到 Navigated 事件处理程序(在 SendMessage 之后):
m_Explorer = new ExplorerListView(m_ShellView);
m_Explorer.ItemExecuted += OnExplorerItemExecuted;
Then add the following classes:
然后添加以下类:
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;
}
}
This should give you an idea of what you would need to do. If you want more than fairly simple events you may want to look for a alternative control, though from what I have seen in the free and low cost areas there are some pretty decent controls but they all have some quirks and will not give a seamless explorer experience.
这应该让您了解您需要做什么。如果您想要的不仅仅是相当简单的事件,您可能想要寻找替代控件,尽管从我在免费和低成本区域中看到的内容来看,有一些相当不错的控件,但它们都有一些怪癖,不会提供无缝浏览器经验。
Remember this code was put together fairly quickly without error handling or comments and ignoring several issues such as multiple selected items, so use it as a guideline and at your own risk.
请记住,此代码是相当快速地组合在一起的,没有错误处理或注释,也没有忽略多个问题(例如多个选定项目),因此将其用作指南并自担风险。
回答by uzbones
LogicNP Software has two controls (FileView and ShComboBox) that do what your looking for: http://www.ssware.com/fldrview.htm
LogicNP Software 有两个控件(FileView 和 ShComboBox)可以满足您的需求:http://www.ssware.com/fldrview.htm
You can download a trial from their page, however it's ~130$ for the license.
您可以从他们的页面下载试用版,但许可证价格约为 130 美元。
回答by ShuggyCoUk
If you are happy being Windows Vista only and wrapping a COMcontrol, IExplorerBrowsermight be acceptable for you needs.
如果您很高兴仅使用 Windows Vista 并包装COM控件,那么IExplorerBrowser可能可以满足您的需求。
This The Code Project articleshows its use within an MFCprogram but at least one other personseems to have got it to work in C# after some effort.
这篇 The Code Project 文章展示了它在MFC程序中的使用,但至少有其他人似乎经过一些努力已经让它在 C# 中工作。
The newer API exposes considerably more programmability than simply intercepting messages, but it is (obviously) useless for older platforms.
较新的 API 比简单地拦截消息公开了更多的可编程性,但它(显然)对旧平台无用。
回答by Matt Warren
Check out this article here, it shows how to do this in .NET and WinForms. Doing it this way gives full-control over what the user sees.
在此处查看这篇文章,它展示了如何在 .NET 和 WinForms 中执行此操作。这样做可以完全控制用户看到的内容。
I've used it in one of my applications and it works really well. You can show icon/details/list view and it stops the user moving to other directories (which is often the problem of showing the standard file/directory dialogs.
我在我的一个应用程序中使用过它,而且效果非常好。您可以显示图标/详细信息/列表视图,它会阻止用户移动到其他目录(这通常是显示标准文件/目录对话框的问题。
I use it to show the screen like the one below below http://img7.imageshack.us/img7/7647/screenshotbaf.png:
我用它来显示如下所示的屏幕http://img7.imageshack.us/img7/7647/screenshotbaf.png:
回答by Grokys
I have written a library that might be able to help you. You can find it at: http://gong-shell.sourceforge.net/
我写了一个图书馆,也许可以帮助你。您可以在以下位置找到它:http: //gong-shell.sourceforge.net/
The control you're looking for is the ShellView. There's tutorials there on how to create a simple Windows Explorer clone in only a few lines too.
您正在寻找的控件是 ShellView。那里也有关于如何仅用几行创建简单的 Windows 资源管理器克隆的教程。
Note for .NET 4.0 users: Gong-shell is currently broken for 4.0. The framework introduced changes in Interop and it will build just fine but cause different issues when interfacing with shell32 (notably the shellicon api, leading to an unmanaged null pointer dereference).
.NET 4.0 用户注意事项:Gong-shell 目前在 4.0 中已损坏。该框架在 Interop 中引入了更改,它可以很好地构建,但在与 shell32 接口时会导致不同的问题(特别是 shellicon api,导致非托管空指针取消引用)。
回答by EricLaw
You may want to look at the ExplorerBrowser object.
您可能需要查看 ExplorerBrowser 对象。
See http://blogs.msdn.com/ieinternals/archive/2009/12/30/Windows-7-Web-Browser-Control-will-not-browse-file-system.aspxfor more details.
有关更多详细信息,请参阅http://blogs.msdn.com/ieinternals/archive/2009/12/30/Windows-7-Web-Browser-Control-will-not-browse-file-system.aspx。