基于CefSharp开发浏览器(十一)增添F11、F12功能
一、前言
最近,一位朋友提出希望完善F11和12功能,因此在这篇文章中,我将分享F11全屏和F12开发者工具的思路以及代码,做个记录的同时也希望对cefsharp开发感兴趣的朋友提供一些思路。
二、WPF使用CefSharp.Winform带来的问题
上文提到,为了更好的使用输入法,我将项目中的CefSharp.WPF转为了CefSharp.WinForms,通过WindowsFormsHost来承载CefSharp.WinForms现在CefSharp的版本为:
<PackageReference Include="CefSharp.WinForms.NETCore" Version="120.1.110" />
同时带来一些问题
1、AllowsTransparency属性不能用
AllowsTransparency只能设置为fasle否则WindowsFormsHost将不可见
可能的原因是Windows Forms本身并不直接支持透明度的特性,而WPF窗体的透明度是通过更先进的图形系统实现的。
因此,当启用WPF窗体的透明度时,可能会导致与Windows Forms控件的嵌入和交互方面的问题,从而使WindowsFormsHost不可用或出现异常。
2、键盘事件和鼠标事件不起作用
当Windows Forms控件在WindowsFormsHost中获得焦点时,WPF的键盘事件可能不会直接传递给嵌入的Windows Forms控件。
为了解决这些问题,需要编写一些额外的代码来将键盘和鼠标事件
三、使用Win32 API拦截和处理特定窗口消息
以下为处理窗口信息的代码,部分代码来源于官方示例
[return: MarshalAs(UnmanagedType.Bool)] [DllImport("user32.dll", SetLastError = true)] private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private void SetupMessageInterceptor() { if (messageInterceptor != null) { messageInterceptor.ReleaseHandle(); messageInterceptor = null; } Task.Run(async () => { try { while (true) { IntPtr chromeWidgetHostHandle; if (ChromiumRenderWidgetHandleFinder.TryFindHandle(this, out chromeWidgetHostHandle)) { messageInterceptor = new ChromiumWidgetNativeWindow(this, chromeWidgetHostHandle); messageInterceptor.OnWndProc(message => { const int WM_MOUSEACTIVATE = 0x0021; const int WM_NCLBUTTONDOWN = 0x00A1; const int WM_DESTROY = 0x0002; const int WM_MOUSEWHEEL = 0x020A; // Render process switch happened, need to find the new handle if (message.Msg == WM_DESTROY) { SetupMessageInterceptor(); return false; } if (message.Msg == WM_MOUSEACTIVATE) { var topLevelWindowHandle = message.WParam; PostMessage(topLevelWindowHandle, WM_NCLBUTTONDOWN, IntPtr.Zero, IntPtr.Zero); } if(message.Msg == WM_MOUSEWHEEL) { int rawDelta = (int)message.WParam.ToInt64(); int delta = rawDelta >> 16; MouseWheelEvent?.Invoke(delta); } return false; }); break; } else { await Task.Delay(10); } } } catch { } }); }
WM_MOUSEACTIVATE 消息,它模拟在非客户区按下鼠标左键的操作。
WM_DESTROY 消息,说明窗口即将被销毁,它调用 SetupMessageInterceptor 方法重新设置消息拦截器。
WM_MOUSEWHEEL 消息,它触发一个事件 (MouseWheelEvent),传递鼠标滚轮的滚动量。
WM_MOUSEWHEEL 为处理浏览器网页缩放的消息,这里增加了MouseWheelEvent当有WM_MOUSEWHEEL 时
触发MouseWheelEvent来处理浏览器缩放,浏览器缩放可以参考 基于CefSharp开发浏览器(六)浏览器网页缩放
四、使用KeyboardHandler处理键盘事件
public class CustomKeyboardHandler: KeyboardHandler { public Action<int> KeyboardCallBack; protected override bool OnKeyEvent(IWebBrowser chromiumWebBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) { if (chromiumWebBrowser.IsBrowserInitialized) { KeyboardCallBack?.Invoke(windowsKeyCode); } return base.OnKeyEvent(chromiumWebBrowser, browser, type, windowsKeyCode, nativeKeyCode, modifiers, isSystemKey); } }
public class CustomWebBrowser : ChromiumWebBrowser { public CustomWebBrowser() { this.KeyboardHandler = new CustomKeyboardHandler(); } }
使用KeyboardHandler的OnKeyEvent方法处理键盘事件如F5,F11,F12等
五、F11全屏
由于程序的主体为一个自定义的TabControl进行的网页多开,直接处理TabControl令其全屏比较麻烦,
这里采用当按下F11时弹出一个WPF窗体来承载现有的CefSharp控件,当按下F11时,获取当前tab页的cefsharp控件,并将其填充到F11窗体中,
再次按下F11时将cefsharp控件 从新填充到tab页中,这样可以保证全屏和非全屏状态下使用的是同一个cefsharp实例。
窗体定义如下:
<Window x:Class="MWebBrowser.View.F11Window" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Title="F11Window" Height="450" Width="800" WindowState="Maximized" WindowStyle="None"> <Grid x:Name="Parent"> </Grid> </Window>
public partial class F11Window : Window { public WindowsFormsHost WebFormsHost { get; } public F11Window() { InitializeComponent(); WebFormsHost = new WindowsFormsHost(); InitData(); } private void InitData() { Parent.Children.Add(WebFormsHost); } }
F11 处理代码如下:
public static void F11(BrowserUserControl browserUserControl, WindowsFormsHost orgWebFormsHost) { DispatcherHelper.UIDispatcher.Invoke(() => { if (f11Window != null) { ExitFullscreen(browserUserControl, orgWebFormsHost); } else { EnterFullscreen(browserUserControl, orgWebFormsHost); } }); } private static void EnterFullscreen(BrowserUserControl browserUserControl, WindowsFormsHost orgWebFormsHost) { originalBounds = new Rect( browserUserControl.CefWebBrowser.Margin.Left, browserUserControl.CefWebBrowser.Margin.Top, browserUserControl.CefWebBrowser.Width, browserUserControl.CefWebBrowser.Height ); f11Window = new F11Window(); orgWebFormsHost.Child.Controls.Remove(browserUserControl); f11Window.WebFormsHost.Child = browserUserControl; f11Window.Show(); Application.Current.MainWindow.Hide(); } private static void ExitFullscreen(BrowserUserControl browserUserControl, WindowsFormsHost orgWebFormsHost) { f11Window.WebFormsHost.Child.Controls.Remove(browserUserControl); orgWebFormsHost.Child = browserUserControl; f11Window.Close(); f11Window = null; Application.Current.MainWindow.Show(); }
六、F12开发者工具
CefSharp提供了两种开发者工具展示方式
1、ShowDevTools
这种直接弹出一个新窗口
2、ShowDevToolsDocked
这种方式将DevTools控件嵌入到Winform的SplitContainer中,代码如下:
public void ShowDevToolsDocked() { this.Invoke(() => { if (browserSplitContainer.Panel2Collapsed) { browserSplitContainer.Panel2Collapsed = false; } Control devToolsControl = GetDevToolsControl(); if (devToolsControl == null || devToolsControl.IsDisposed) { CefWebBrowser.ShowDevToolsDocked(browserSplitContainer.Panel2, nameof(devToolsControl), DockStyle.Fill); } }); }
七、F12开发者工具
gitee地址:https://gitee.com/sirius_machao/mweb-browser
github地址:https://github.com/sirius-chao/MWebBrowser
项目邀请:如对该项目有兴趣,欢迎联系我共同开发!!!