警告:長時間使用後大量的代碼。
將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;
}
}
這應該讓你知道你需要做什麼。如果你想要的不僅僅是相當簡單的事件,你可能想要尋找一種替代控制,儘管從我在免費和低成本領域看到的有一些相當不錯的控制,但它們都有一些怪癖,並且不會給無縫探測器經驗。
請記住,此代碼相當快,沒有錯誤處理或評論,並忽略了多個問題,如多個選定的項目,所以用它作爲指導,風險自負。
我發現有用的是(商業)[ShellBrowser組件](http://www.jam-software.com/shellbrowser_net/?language=EN)。 – 2013-11-11 12:44:15