我在项目中使用WebBrowser
控件。
我注意到如果用户按下右键 - >刷新
Navigating
LoadCompleted
没有打电话。
另一个奇怪的事情是,在我按下刷新并点击webbrowser中的另一个链接后,LoadCompleted
也没有被调用。
这是我在开始时浏览页面的方式:
nrowser.Navigate(MYPAGE);
知道可能出现什么问题?
我可以使用JavaScript
修复它吗?
答案 0 :(得分:3)
您可以处理刷新并将其与导航区分开来。通过一些练习,可以通过处理DOM document.onreadystatechange
和window.onunload
事件以及底层WebBrowser ActiveX控件的NavigateComplete2
和DownloadBegin
事件来完成。
NavigateComplete2
对于顶级WebBrowser
对象不会被触发,但在导航时会发生这种情况。对于任何下载,导航或刷新活动,DownloadBegin
总是被解雇,可能多次。导航或刷新前会window.onunload
被触发,导航或刷新期间会document.onreadystatechange
被触发。其余的是跟踪状态转换的问题。这是一个工作示例(仍可能包含错误)。
<强> C#:强>
using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
namespace WpfWebBrowser
{
public partial class MainWindow : Window
{
bool navigating = false;
bool loading = false;
bool loaded = false;
public MainWindow()
{
SetBrowserFeatureControl();
InitializeComponent();
this.Loaded += (s, e) =>
{
var axWebBrowser = (SHDocVw.WebBrowser)GetActiveXInstance(this.webBrowser);
axWebBrowser.DownloadBegin += delegate
{
HandleDownloadActivity();
};
axWebBrowser.NavigateComplete2 += delegate(object pDisp, ref object URL)
{
// top frame?
if (Object.ReferenceEquals(axWebBrowser, pDisp))
{
this.navigating = true;
HandleDownloadActivity();
}
};
this.webBrowser.Navigate("http://example.com");
};
}
// handler for document.readyState == "complete"
void DomDocumetCompleteHandler(dynamic domDocument)
{
dynamic domWindow = domDocument.parentWindow;
domWindow.attachEvent("onunload", new DomEventHandler(delegate
{
this.loaded = false;
this.loading = false;
}));
var navigated = this.navigating;
this.navigating = false;
this.loaded = true;
this.loading = false;
MessageBox.Show(navigated ? "Navigated" : "Refreshed");
}
void HandleDownloadActivity()
{
dynamic domDocument = this.webBrowser.Document;
if (domDocument == null)
return;
if (loading || loaded)
return;
this.loading = true;
if (domDocument.readyState == "complete")
{
DomDocumetCompleteHandler(domDocument);
}
else
{
DomEventHandler handler = null;
handler = new DomEventHandler(delegate
{
if (domDocument.readyState == "complete")
{
domDocument.detachEvent("onreadystatechange", handler);
DomDocumetCompleteHandler(domDocument);
}
});
domDocument.attachEvent("onreadystatechange", handler);
}
}
/// <summary>
/// Get the underlying WebBrowser ActiveX object;
/// this code depends on SHDocVw.dll COM interop assembly,
/// generate SHDocVw.dll: "tlbimp.exe ieframe.dll",
/// and add as a reference to the project
/// </summary>
static object GetActiveXInstance(WebBrowser wb)
{
return wb.GetType().InvokeMember("ActiveXInstance",
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, wb, new object[] { }) as SHDocVw.WebBrowser;
}
/// <summary>
/// EventHandler - adaptor to call C# back from JavaScript or DOM event handlers
/// </summary>
[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch)]
public class DomEventHandler
{
[ComVisible(false)]
public delegate void Callback(ref object result, object[] args);
[ComVisible(false)]
private Callback _callback;
[DispId(0)]
public object Method(params object[] args)
{
var result = Type.Missing; // Type.Missing is "undefined" in JavaScript
_callback(ref result, args);
return result;
}
public DomEventHandler(Callback callback)
{
_callback = callback;
}
}
/// <summary>
/// WebBrowser version control
/// http://msdn.microsoft.com/en-us/library/ee330730(v=vs.85).aspx#browser_emulation
/// </summary>
void SetBrowserFeatureControl()
{
// http://msdn.microsoft.com/en-us/library/ee330720(v=vs.85).aspx
// FeatureControl settings are per-process
var fileName = System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName);
// make the control is not running inside Visual Studio Designer
if (String.Compare(fileName, "devenv.exe", true) == 0 || String.Compare(fileName, "XDesProc.exe", true) == 0)
return;
// Webpages containing standards-based !DOCTYPE directives are displayed in IE9/IE10 Standards mode.
SetBrowserFeatureControlKey("FEATURE_BROWSER_EMULATION", fileName, 9000);
}
void SetBrowserFeatureControlKey(string feature, string appName, uint value)
{
using (var key = Registry.CurrentUser.CreateSubKey(
String.Concat(@"Software\Microsoft\Internet Explorer\Main\FeatureControl\", feature),
RegistryKeyPermissionCheck.ReadWriteSubTree))
{
key.SetValue(appName, (UInt32)value, RegistryValueKind.DWord);
}
}
}
}
<强> XAML:强>
<Window x:Class="WpfWebBrowser.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Width="800" Height="600">
<WebBrowser Name="webBrowser"/>
</Window>
答案 1 :(得分:0)
感谢Noseratio,它很棒。我将您所做的事情包装到一个对象中,您可以将Web浏览器传递到并处理事件。
public class WebBrowserHook
{
private bool _isNavigating = false;
private bool _isLoading = false;
private bool _isLoaded = false;
private WebBrowser _webBrowser = null;
public WebBrowserHook(WebBrowser webBrowser)
{
_webBrowser = webBrowser;
_webBrowser.Loaded += (s, e) =>
{
try
{
var axWebBrowser = (SHDocVw.WebBrowser)GetActiveXInstance(this._webBrowser);
axWebBrowser.DownloadBegin += delegate
{
HandleDownloadActivity();
};
axWebBrowser.NavigateComplete2 += delegate(object pDisp, ref object URL)
{
// top frame?
if (Object.ReferenceEquals(axWebBrowser, pDisp))
{
this._isNavigating = true;
HandleDownloadActivity();
}
};
}
catch (Exception ex)
{
AppLog.LogException(ex);
}
};
}
public event EventHandler Navigated;
public event EventHandler Refreshed;
public WebBrowser Browser
{
get{return _webBrowser;}
}
// handler for document.readyState == "complete"
private void DomDocumentCompleteHandler(mshtml.HTMLDocument domDocument)
{
try
{
mshtml.HTMLWindow2 domWindow = (mshtml.HTMLWindow2)domDocument.parentWindow;
domWindow.attachEvent("onunload", new DomEventHandler(delegate
{
this._isLoaded = false;
this._isLoading = false;
}));
var navigated = this._isNavigating;
this._isNavigating = false;
this._isLoaded = true;
this._isLoading = false;
if (navigated)
{
if (Navigated != null)
Navigated(this, new EventArgs());
}
else
{
if (Refreshed != null)
Refreshed(this, new EventArgs());
}
}
catch (Exception ex)
{
AppLog.LogException(ex);
}
}
private void HandleDownloadActivity()
{
try
{
mshtml.HTMLDocument domDocument = (mshtml.HTMLDocument)this._webBrowser.Document;
if (domDocument == null)
return;
if (_isLoading || _isLoaded)
return;
this._isLoading = true;
if (domDocument.readyState == "complete")
{
DomDocumentCompleteHandler(domDocument);
}
else
{
DomEventHandler handler = null;
handler = new DomEventHandler(delegate
{
if (domDocument.readyState == "complete")
{
domDocument.detachEvent("onreadystatechange", handler);
DomDocumentCompleteHandler(domDocument);
}
});
domDocument.attachEvent("onreadystatechange", handler);
}
}
catch (Exception ex)
{
AppLog.LogException(ex);
}
}
/// <summary>
/// Get the underlying WebBrowser ActiveX object;
/// this code depends on SHDocVw.dll COM interop assembly,
/// generate SHDocVw.dll: "tlbimp.exe ieframe.dll",
/// and add as a reference to the project
/// </summary>
private static object GetActiveXInstance(WebBrowser wb)
{
return wb.GetType().InvokeMember("ActiveXInstance",
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, wb, new object[] { }) as SHDocVw.WebBrowser;
}
/// <summary>
/// EventHandler - adaptor to call C# back from JavaScript or DOM event handlers
/// </summary>
[ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch)]
public class DomEventHandler
{
[ComVisible(false)]
public delegate void Callback(ref object result, object[] args);
[ComVisible(false)]
private Callback _callback;
[DispId(0)]
public object Method(params object[] args)
{
var result = Type.Missing; // Type.Missing is "undefined" in JavaScript
_callback(ref result, args);
return result;
}
public DomEventHandler(Callback callback)
{
_callback = callback;
}
}
}