WPF WebBrowser控件 – 如何抑制脚本错误?
我在这里find了类似的问题:
如何在使用WPF WebBrowser控件时抑制脚本错误?
但是这些解决scheme都不适合我。 我需要停止显示popup窗口,因为我使用WebBrowser来自动执行网站上的pipe理任务。
SuppressScriptErrors
似乎不是我的WebControl上的可用属性:(
这是一个C#程序,可以把WPF的WebBrowser
置于静音模式。 你不能在WebBrowser初始化时调用它,因为它太早了,而是在导航发生之后。 这是一个带有wbMain WebBrowser组件的WPF示例应用程序:
public partial class Window1 : Window { public Window1() { InitializeComponent(); wbMain.Navigated += new NavigatedEventHandler(wbMain_Navigated); } void wbMain_Navigated(object sender, NavigationEventArgs e) { SetSilent(wbMain, true); // make it silent } private void button1_Click(object sender, RoutedEventArgs e) { wbMain.Navigate(new Uri("... some url...")); } } public static void SetSilent(WebBrowser browser, bool silent) { if (browser == null) throw new ArgumentNullException("browser"); // get an IWebBrowser2 from the document IOleServiceProvider sp = browser.Document as IOleServiceProvider; if (sp != null) { Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046"); Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"); object webBrowser; sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser); if (webBrowser != null) { webBrowser.GetType().InvokeMember("Silent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty, null, webBrowser, new object[] { silent }); } } } [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IOleServiceProvider { [PreserveSig] int QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject); }
认为这里添加是相关的。 还有另外一个选项,可以访问WPF WebBrowser的底层WebBorwser ActiveX控件及其他不可访问的方法和事件 。 我刚刚在几天前发现它。 这是非常简单的,不需要WB上的初始导航:
dynamic activeX = this.WB.GetType().InvokeMember("ActiveXInstance", BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, this.WB, new object[] { }); activeX.Silent = true;
当然,这种方法在将来的框架版本中可能不适用,但其他任何未公开的方法都是如此。 到目前为止,它已经在.NET 3.0以后。 更多的细节在这里工作的代码示例。
感谢Simon Mourier以优雅的方式解决这个问题。 我做了一点改进,把西蒙的解决scheme封装在附属的财产中。
在我的应用程序中,我使用WebBrowser控件databounded查看模型,网页浏览器可能会隐藏在不活动的TabItem,所以我必须检查它已被加载和浏览之前,设置JavaScript错误沉默。 而且这个设置应该只做一次,所以在设置之后,我释放了钩子事件。
XAML代码:
<TabControl xmlns:b="clr-namespace:MyApplication.Behaviors"> <TabItem Header="foo">...</TabItem> <TabItem Header="Google map"> <WebBrowser b:BindableSource="{Binding Path=MapUrl}" b:DisableJavascriptErrors="True" /> </TabItem> </TabControl>
行为代码:
using System; using System.Reflection; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Controls; using System.Windows.Navigation; namespace MyApplication.Behaviors { public class WebBrowserBehavior { private static readonly Type OwnerType = typeof (WebBrowserBehavior); #region BindableSource public static readonly DependencyProperty BindableSourceProperty = DependencyProperty.RegisterAttached( "BindableSource", typeof(string), OwnerType, new UIPropertyMetadata(OnBindableSourcePropertyChanged)); [AttachedPropertyBrowsableForType(typeof(WebBrowser))] public static string GetBindableSource(DependencyObject obj) { return (string)obj.GetValue(BindableSourceProperty); } [AttachedPropertyBrowsableForType(typeof(WebBrowser))] public static void SetBindableSource(DependencyObject obj, string value) { obj.SetValue(BindableSourceProperty, value); } public static void OnBindableSourcePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var browser = d as WebBrowser; if (browser == null) return; browser.Source = (e.NewValue != null) ? new Uri(e.NewValue.ToString()) : null; } #endregion #region DisableJavascriptErrors #region SilentJavascriptErrorsContext (private DP) private static readonly DependencyPropertyKey SilentJavascriptErrorsContextKey = DependencyProperty.RegisterAttachedReadOnly( "SilentJavascriptErrorsContext", typeof (SilentJavascriptErrorsContext), OwnerType, new FrameworkPropertyMetadata(null)); private static void SetSilentJavascriptErrorsContext(DependencyObject depObj, SilentJavascriptErrorsContext value) { depObj.SetValue(SilentJavascriptErrorsContextKey, value); } private static SilentJavascriptErrorsContext GetSilentJavascriptErrorsContext(DependencyObject depObj) { return (SilentJavascriptErrorsContext) depObj.GetValue(SilentJavascriptErrorsContextKey.DependencyProperty); } #endregion public static readonly DependencyProperty DisableJavascriptErrorsProperty = DependencyProperty.RegisterAttached( "DisableJavascriptErrors", typeof (bool), OwnerType, new FrameworkPropertyMetadata(OnDisableJavascriptErrorsChangedCallback)); [AttachedPropertyBrowsableForType(typeof(WebBrowser))] public static void SetDisableJavascriptErrors(DependencyObject depObj, bool value) { depObj.SetValue(DisableJavascriptErrorsProperty, value); } [AttachedPropertyBrowsableForType(typeof(WebBrowser))] public static bool GetDisableJavascriptErrors(DependencyObject depObj) { return (bool)depObj.GetValue(DisableJavascriptErrorsProperty); } private static void OnDisableJavascriptErrorsChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { var webBrowser = d as WebBrowser; if (webBrowser == null) return; if (Equals(e.OldValue, e.NewValue)) return; var context = GetSilentJavascriptErrorsContext(webBrowser); if (context != null) { context.Dispose(); } if (e.NewValue != null) { context = new SilentJavascriptErrorsContext(webBrowser); SetSilentJavascriptErrorsContext(webBrowser, context); } else { SetSilentJavascriptErrorsContext(webBrowser, null); } } private class SilentJavascriptErrorsContext : IDisposable { private bool? _silent; private readonly WebBrowser _webBrowser; public SilentJavascriptErrorsContext(WebBrowser webBrowser) { _silent = new bool?(); _webBrowser = webBrowser; _webBrowser.Loaded += OnWebBrowserLoaded; _webBrowser.Navigated += OnWebBrowserNavigated; } private void OnWebBrowserLoaded(object sender, RoutedEventArgs e) { if (!_silent.HasValue) return; SetSilent(); } private void OnWebBrowserNavigated(object sender, NavigationEventArgs e) { var webBrowser = (WebBrowser)sender; if (!_silent.HasValue) { _silent = GetDisableJavascriptErrors(webBrowser); } if (!webBrowser.IsLoaded) return; SetSilent(); } /// <summary> /// Solution by Simon Mourier on StackOverflow /// http://stackoverflow.com/a/6198700/741414 /// </summary> private void SetSilent() { _webBrowser.Loaded -= OnWebBrowserLoaded; _webBrowser.Navigated -= OnWebBrowserNavigated; // get an IWebBrowser2 from the document var sp = _webBrowser.Document as IOleServiceProvider; if (sp != null) { var IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046"); var IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"); object webBrowser2; sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out webBrowser2); if (webBrowser2 != null) { webBrowser2.GetType().InvokeMember( "Silent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.PutDispProperty, null, webBrowser2, new object[] { _silent }); } } } [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] private interface IOleServiceProvider { [PreserveSig] int QueryService([In] ref Guid guidService, [In] ref Guid riid, [MarshalAs(UnmanagedType.IDispatch)] out object ppvObject); } public void Dispose() { if (_webBrowser != null) { _webBrowser.Loaded -= OnWebBrowserLoaded; _webBrowser.Navigated -= OnWebBrowserNavigated; } } } #endregion } }
@SimonMourier的回答不适合我,但是这样做:
public void HideScriptErrors(WebBrowser wb, bool Hide) { FieldInfo fiComWebBrowser = typeof(WebBrowser) .GetField("_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic); if (fiComWebBrowser == null) return; object objComWebBrowser = fiComWebBrowser.GetValue(wb); if (objComWebBrowser == null) return; objComWebBrowser.GetType().InvokeMember( "Silent", BindingFlags.SetProperty, null, objComWebBrowser, new object[] { Hide }); }
请注意,我从这里得到它。
我还发现了一个有趣的方法来禁用JavaScript错误。 但是由于使用了优雅的dynamictypes,所以至less需要使用.Net Framework 4.0。
您需要订阅WebBrowser元素的LoadCompleted事件:
<WebBrowser x:Name="Browser" LoadCompleted="Browser_OnLoadCompleted" />
之后,您需要编写一个如下所示的事件处理程序:
void Browser_OnLoadCompleted(object sender, NavigationEventArgs e) { var browser = sender as WebBrowser; if (browser == null || browser.Document == null) return; dynamic document = browser.Document; if (document.readyState != "complete") return; dynamic script = document.createElement("script"); script.type = @"text/javascript"; script.text = @"window.onerror = function(msg,url,line){return true;}"; document.head.appendChild(script); }
你可以看看我的解决scheme: 在Silverlight中禁用WebBrowser控件中的JavaScript错误 – 它适用于Silverlight,但也适用于WPF。
那么如果有必要,我会去WinformHost
并添加WebBrowser控制,并使用它。
您也可以在WinformHost
轻松完成这些任务, WinformHost
也是因为我已经完成了整个应用程序的捆绑工作
这里是一个不使用reflection的例子。
/// <summary> /// Gets an interop web browser. /// </summary> /// <param name="browser"></param> /// <returns></returns> public static SHDocVw.WebBrowser GetInteropWebBrowser(this WebBrowser browser) { Guid serviceGuid = new Guid("0002DF05-0000-0000-C000-000000000046"); Guid iid = typeof(SHDocVw.IWebBrowser2).GUID; Interop.IServiceProvider serviceProvider = (Interop.IServiceProvider)browser.Document; SHDocVw.IWebBrowser2 browser2 = (SHDocVw.IWebBrowser2)serviceProvider.QueryService(ref serviceGuid, ref iid); SHDocVw.WebBrowser wb = (SHDocVw.WebBrowser)browser2; return wb; } /// <summary> /// Disables script errors for the browser. /// </summary> /// <param name="browser"></param> /// <param name="silent"></param> public static void SetSilent(this WebBrowser browser, bool silent) { SHDocVw.WebBrowser browser2 = browser.GetInteropWebBrowser(); if (browser2 != null) browser2.Silent = silent; } /// <summary> /// Provides the COM interface for the IServiceProvider. /// </summary> [ComImport, Guid("6D5140C1-7436-11CE-8034-00AA006009FA"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IServiceProvider { /// <summary> /// Queries the service. /// </summary> /// <param name="serviceGuid"></param> /// <param name="riid"></param> /// <returns></returns> [return: MarshalAs(UnmanagedType.IUnknown)] object QueryService(ref Guid serviceGuid, ref Guid riid); }
然后在托pipe浏览器控件的视图的构造函数中:
Browser.Navigated += (s, e) => { Browser.SetSilent(true); };
真的很简单,谢谢你的解决scheme。
Dim sb As New StringBuilder sb.Append("<html>") sb.Append("<head>") sb.Append("</head") sb.Append("<body>") sb.Append("<iframe src ='" + url + "' height='" + webBrowser1.Height + "' width='" + webBrowser1.Width + "'></iframe>") sb.Append("</body") sb.Append("</html>") WebBrowser1.DocumentText = sb.ToString