我正在使用 WPF System.Windows.Controls.WebBrowser
控件来显示从服务下载的一些 HTML 内容。有时,HTML 包含应该可点击的 URL("a"元素)。
默认情况下,单击此类 URL 时,它将在 Internet Explorer 中打开。我希望它们在默认浏览器中打开。
注意 我专门讨论的是 WPF WebBrowser。WinForms 浏览器有大量的解决方案,但它们不适用于 WPF 浏览器。
最常见的"解决方案"是处理 Navigating 事件,取消它,然后使用 URL 做你自己的事情。这不起作用,因为当我单击 HTML 中的链接时不会调用 Navigating 事件。
我发现的另一种解决方案似乎确实有效,但有时只是出于某种奇怪的原因:https://stackoverflow.com/a/9111151/573249
我现在有以下代码,使用上面链接中的方法:
private void WebBrowser_OnNavigating(object sender, NavigatingCancelEventArgs e)
{
// Just for demonstration purposes
// This is NOT CALLED when a link is clicked
Debug.WriteLine("> Navigating called.");
if (e.Uri == null)
{
Debug.WriteLine(">> URI was null.");
return;
}
e.Cancel = true;
Process.Start(e.Uri.AbsolutePath);
Debug.WriteLine(">> Navigation cancelled and opened in default browser.");
}
private void WebBrowser_OnLoadCompleted(object sender, NavigationEventArgs e)
{
Debug.WriteLine("> LoadCompleted called.");
mshtml.HTMLDocument doc;
doc = (mshtml.HTMLDocument) webBrowser.Document;
mshtml.HTMLDocumentEvents2_Event iEvent;
iEvent = (mshtml.HTMLDocumentEvents2_Event) doc;
iEvent.onclick += new mshtml.HTMLDocumentEvents2_onclickEventHandler(ClickEventHandler);
Debug.WriteLine("> LoadCompleted finished!");
Debug.WriteLine("------");
}
private bool ClickEventHandler(mshtml.IHTMLEventObj e)
{
// This finally opens the URL in the default browser
// The method is called only 20% of the time.
Debug.WriteLine(">> Click event handler called.");
var a = (mshtml.HTMLAnchorElement) e.srcElement;
Process.Start(a.href);
return false;
}
当我单击一个链接时,它似乎在 20% 的时间内有效。在这些情况下,将调用"ClickEventHandler",并在默认浏览器中打开链接。在其他 80% 的情况下,永远不会调用"ClickEventHandler"(并且链接在 IE 中打开),即使"OnLoadComplete"无异常地完成也是如此。
似乎没有模式,尽管当它失败一次时,它似乎在同一文档中永远失败,直到我重新加载 HTML。重新加载后,它恢复到20%的工作机会。
这是怎么回事?
我遇到了完全相同的问题。OnMouseDown 事件只是偶尔触发。我无法弄清楚这一点。我只是放弃了,改用了WinForm WebBrowser。然后,您不需要MSHTML的参考,但需要System.Windows.Forms和System.Windows.Interactivity的参考。
XAML
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
<WindowsFormsHost>
<wf:WebBrowser x:Name="WbThumbs"/>
</WindowsFormsHost>
C#
public WMain() {
System.Windows.Forms.Application.EnableVisualStyles();
InitializeComponent();
WbThumbs.DocumentCompleted += WbThumbsOnDocumentCompleted;
WbThumbs.Navigate("www.blabla.com");
}
private void WbThumbsOnDocumentCompleted(object sender, System.Windows.Forms.WebBrowserDocumentCompletedEventArgs e) {
if (WbThumbs.Document == null) return;
WbThumbs.Document.Click += WbThumbs_Click;
}
private void WbThumbs_Click(object sender, System.Windows.Forms.HtmlElementEventArgs e) {
var doc = WbThumbs.Document;
var src = doc?.GetElementFromPoint(e.ClientMousePosition);
if (src == null) return;
//...
}
面临根本不触发"打开链接"事件的问题。在我的特定情况下,这是由于属性target
设置为_blank
文档中的所有<a>
元素。
通过在调用 NavigateToString 之前将其更改为_self
字符串表示形式的默认值来解决。
从:
<a href="https://something.something" target="_blank">
自:
<a href="https://something.something" target="_self">
PS AFAIK 将目标指定为"_self"是多余的,在这种情况下,可以省略该属性,但替换起来更简单。
我在 SO 上检查了很多相关回复,但唯一对我有用的是鼻子比率的答案 https://stackoverflow.com/a/20902928几年前,我在其他项目中使用了IE,其中IE集成到了WPF应用程序中。现在我正在使用WebBrowser,对我来说,它就像一个魅力。这是我的代码:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(DWebBrowserEvents2))]
public class WebBrowserEventSink : DWebBrowserEvents2
{
System.Runtime.InteropServices.ComTypes.IConnectionPoint sinkCp;
int sinkCookie = int.MaxValue;
public void Connect(System.Windows.Controls.WebBrowser webBrowser)
{
if (sinkCookie != int.MaxValue)
throw new InvalidOperationException();
var activeXInstance = webBrowser.GetType().InvokeMember("ActiveXInstance",
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
null, webBrowser, new object[] { }) as WebBrowser;
// ReSharper disable once SuspiciousTypeConversion.Global
var cpc = (System.Runtime.InteropServices.ComTypes.IConnectionPointContainer)activeXInstance;
var guid = typeof(DWebBrowserEvents2).GUID;
if (cpc != null)
{
System.Runtime.InteropServices.ComTypes.IConnectionPoint cp;
cpc.FindConnectionPoint(ref guid, out cp);
cp.Advise(this, out sinkCookie);
}
}
public void Connect(InternetExplorer webBrowser)
{
if (sinkCookie != int.MaxValue)
throw new InvalidOperationException();
// ReSharper disable SuspiciousTypeConversion.Global
var activeXInstance = webBrowser as WebBrowser;
var cpc = (System.Runtime.InteropServices.ComTypes.IConnectionPointContainer)activeXInstance;
// ReSharper restore SuspiciousTypeConversion.Global
var guid = typeof(DWebBrowserEvents2).GUID;
if (cpc != null)
{
System.Runtime.InteropServices.ComTypes.IConnectionPoint cp;
cpc.FindConnectionPoint(ref guid, out cp);
cp.Advise(this, out sinkCookie);
}
}
public void Disconnect()
{
if (sinkCookie == int.MaxValue)
throw new InvalidOperationException();
if (sinkCp != null)
{
sinkCp.Unadvise(sinkCookie);
sinkCookie = int.MaxValue;
}
sinkCp = null;
}
#region SHDocVw.DWebBrowserEvents2
public void StatusTextChange(string text)
{
}
public void ProgressChange(int progress, int progressMax)
{
}
public void CommandStateChange(int command, bool enable)
{
}
public void DownloadBegin()
{
}
public void DownloadComplete()
{
}
public void TitleChange(string text)
{
}
public void PropertyChange(string szProperty)
{
}
public void BeforeNavigate2(object pDisp, ref object url, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel)
{
InvokeBeforeNavigate2(pDisp, ref url, ref flags, ref targetFrameName, ref postData, ref headers, ref cancel);
}
public void NewWindow2(ref object ppDisp, ref bool cancel)
{
}
public void NavigateComplete2(object pDisp, ref object url)
{
}
public void DocumentComplete(object pDisp, ref object url)
{
InvokeDocumentComplete(pDisp, ref url);
}
public void OnQuit()
{
}
public void OnVisible(bool visible)
{
}
public void OnToolBar(bool toolBar)
{
}
public void OnMenuBar(bool menuBar)
{
}
public void OnStatusBar(bool statusBar)
{
}
public void OnFullScreen(bool fullScreen)
{
}
public void OnTheaterMode(bool theaterMode)
{
}
public void WindowSetResizable(bool resizable)
{
}
public void WindowSetLeft(int left)
{
}
public void WindowSetTop(int top)
{
}
public void WindowSetWidth(int width)
{
}
public void WindowSetHeight(int height)
{
}
public void WindowClosing(bool isChildWindow, ref bool cancel)
{
}
public void ClientToHostWindow(ref int cx, ref int cy)
{
}
public void SetSecureLockIcon(int secureLockIcon)
{
}
public void FileDownload(bool activeDocument, ref bool cancel)
{
}
public void NavigateError(object pDisp, ref object url, ref object frame, ref object statusCode, ref bool cancel)
{
}
public void PrintTemplateInstantiation(object pDisp)
{
}
public void PrintTemplateTeardown(object pDisp)
{
}
public void UpdatePageStatus(object pDisp, ref object nPage, ref object fDone)
{
}
public void PrivacyImpactedStateChange(bool bImpacted)
{
}
public void NewWindow3(ref object ppDisp, ref bool cancel, uint dwFlags, string bstrUrlContext, string bstrUrl)
{
InvokeNewWindow3(ref ppDisp, ref cancel, dwFlags, bstrUrlContext, bstrUrl);
}
public void SetPhishingFilterStatus(int phishingFilterStatus)
{
}
public void WindowStateChanged(uint dwWindowStateFlags, uint dwValidFlagsMask)
{
}
public void NewProcess(int lCauseFlag, object pWb2, ref bool cancel)
{
}
public void ThirdPartyUrlBlocked(ref object url, uint dwCount)
{
}
public void RedirectXDomainBlocked(object pDisp, ref object startUrl, ref object redirectUrl, ref object frame, ref object statusCode)
{
}
public void BeforeScriptExecute(object pDispWindow)
{
}
public void WebWorkerStarted(uint dwUniqueId, string bstrWorkerLabel)
{
}
public void WebWorkerFinsihed(uint dwUniqueId)
{
}
#endregion
#region events
#region NewWindow3
internal delegate void NewWindow3EventHandler(ref object ppDisp, ref bool cancel, uint dwFlags, string bstrUrlContext, string bstrUrl);
internal event NewWindow3EventHandler NewWindow3Event;
internal void InvokeNewWindow3(ref object ppDisp, ref bool cancel, uint dwFlags, string bstrUrlContext, string bstrUrl)
{
var handler = NewWindow3Event;
if (handler != null)
handler(ref ppDisp, ref cancel, dwFlags, bstrUrlContext, bstrUrl);
}
#endregion
#region before navigate 2
internal delegate void BeforeNavigate2EventHandler(object pDisp, ref object url, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel);
internal event BeforeNavigate2EventHandler BeforeNavigate2Event;
internal void InvokeBeforeNavigate2(object pDisp, ref object url, ref object flags, ref object targetFrameName, ref object postData, ref object headers, ref bool cancel)
{
var handler = BeforeNavigate2Event;
if (handler != null)
handler(pDisp, ref url, ref flags, ref targetFrameName, ref postData, ref headers, ref cancel);
}
#endregion
#region DocumentComplete
internal delegate void DocumentCompleteEventHandler(object pDisp, ref object url);
internal event DocumentCompleteEventHandler DocumentCompleteEvent;
internal void InvokeDocumentComplete(object pDisp, ref object url)
{
var handler = DocumentCompleteEvent;
if (handler != null)
handler(pDisp, ref url);
}
#endregion
#endregion
}
这是用法:
public TextViewer()
{
Loaded += TextViewer_OnLoaded;
InitializeComponent();
}
private void TextViewer_OnLoaded(object sender, RoutedEventArgs e)
{
if (brwHtml != null)
{
_sink = new WebBrowserEventSink();
_sink.Connect(brwHtml);
_sink.NewWindow3Event += Sink_OnNewWindow3Event;
}
}
private void Sink_OnNewWindow3Event(ref object ppDisp, ref bool cancel, uint dwFlags, string bstrUrlContext, string bstrUrl)
{
cancel = true;
System.Diagnostics.Process.Start(bstrUrl);
}