CefSharp 实现多标签页 调用ChromiumWebBrowser的SetAsPopup()后浏览页卡死,的另一种解决方案
最进想使用WPF+CefSharp实现浏览器多标签页, 所以直接实现ILifeSpanHandler接口添加窗口处理,但是在OnBeforePopup方法中,设置好窗口句柄后触发弹出新页时发现一个尴尬的问题:
问题
OnBeforePopup方法中调用SetAsPopup()方法,会导致浏览器首页卡死(仅在WPF中发现,Winform不会),新窗口页正常操作(后续窗口页中都不会有这个问题)。
如果不调用SetAsPopup()方法,会导致新窗口页的事件全部失效(处理器不会),比如TitleChanged事件在浏览器标题更改后,需要及时更新浏览器标题到控件上,这样就无法触发浏览器标题更改事件(TitleChanged)。
解决办法
既然调用SetAsPopup会卡死,那我们就不调用,但是不调用SetAsPopup会导致新打开的窗口页事件丢失,那就想办法手动实现这个方法的一部分功能(赋值事件、处理器等信息)。
SetAsPopup方法中最后有一个out IWebBrowser newBrowser 参数,这个参数作用是新打开窗口页的浏览器对象需不需要我们提供,一般设置这个参数为null,不需要我们提供。
如果不为null时,新窗口浏览器对象将使用newBrowser作为新窗口页的浏览器对象。
public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess,
out IWebBrowser newBrowser) {
所以在方法中显示给newBrowser赋上值,(相当于实现一部分SetAsPopup方法工作...):
public class CefLifeSpanHandler : ILifeSpanHandler { //省略其他ILifeSpanHandler接口实现方法...
//构造函数传入窗口对象,方便控制窗口中的控件 MainWindow mainWindow; public CefLifeSpanHandler(MainWindow _mainWindow) { this.mainWindow = _mainWindow; } //浏览器新窗口弹出时调用方法 public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) { //为新弹窗创建一个浏览器对象 ChromiumWebBrowser newChromeBrowser = new ChromiumWebBrowser(); //为新弹窗复制窗口处理 newBrowser.LifeSpanHandler = this; //省略赋值其他处理器... //赋值标题修改事件 newChromeBrowser.TitleChanged += mainWindow.Chrome_TitleChange; //省略赋值其他事件.... //留下创建的浏览器对象引用,windowList(List<ChromiumWebBrowser>)是在MainWindow下声明的变量,窗口关闭时要释放,不然退出程序会跨线程访问控件错误 mainWindow.windowList.Add(newChromeBrowser); //为浏览器赋值 newBrowser = newChromeBrowser; //.... return false; } }
到这里,在新窗口页打开时,即触发了标题修改事件,又没有导致浏览器首页卡死,但在关闭窗口时,会报跨线程访问错误,需要在窗口关闭时释放掉创建的窗口浏览器对象:
private void Window_Closed(object sender, EventArgs e) { //释放每一个chrome窗口资源 for (int i = 0; i < windowList.Count; i ++) { windowList[i].Dispatcher.Invoke(delegate { windowList[i].Dispose(); }); } }
到这里,基本上解决了阻止浏览器弹窗时调用 SetAsPopup 首页卡死问题。
如果本人有理解不对的地方,希望前辈们评论区指导,非常感谢!