WinForm中CefSharp控件在Microsoft Surface触摸场景下焦点异常问题求助
问题描述
我在Microsoft Surface平板上使用触摸输入操作WinForm应用时遇到问题,已知WinForm原生不支持触摸输入,但希望找到可行的解决方法。我的WinForm应用窗体内包含两个控件:一个简单的列表框(List Box)和CefSharp ChromiumWebBrowser控件。
应用启动后HTML正常加载,使用普通鼠标操作时一切正常,点击文本框即可正常输入内容。但在Surface设备上使用触摸操作执行相同步骤时,ChromiumWebBrowser控件似乎无法获得焦点:浏览器内部的HTML文本框已被激活,但WinForm层面的ChromiumWebBrowser控件未获得焦点,因此输入时没有内容传递到文本框中。
我尝试通过WinForm的C#代码手动设置焦点,但触摸模式下除了Enter事件仅触发一次外,其余点击类事件都不会触发,目前没有可行的规避思路。
业务场景要求用户可以点击浏览器控件外的区域完成其他操作,之后返回浏览器即可正常在文本框中输入内容。
问题复现动图:
已有代码
private void Form1_Load(object sender, EventArgs e) { var asForm = System.Windows.Automation.AutomationElement.FromHandle(this.Handle); wbMap = initBrowser(); this.panel1.Enabled = true; this.panel1.Controls.Add(wbMap); wbMap.FrameLoadEnd += WbMap_FrameLoadEnd; wbMap.Click += WbMap_Click; wbMap.Enter += WbMap_Enter; wbMap.MouseClick += WbMap_MouseClick; wbMap.DoubleClick += WbMap_DoubleClick; wbMap.Leave += WbMap_Leave; wbMap.LostFocus += WbMap_LostFocus; wbMap.GotFocus += WbMap_GotFocus; wbMap.PreviewKeyDown += WbMap_PreviewKeyDown; } private ChromiumWebBrowser initBrowser() { ChromiumWebBrowser wbMap = new ChromiumWebBrowser(); BrowserSettings browserSettings = new BrowserSettings(); browserSettings.FileAccessFromFileUrls = CefState.Enabled; browserSettings.UniversalAccessFromFileUrls = CefState.Enabled; wbMap.BrowserSettings = browserSettings; wbMap.LoadHtml("<!DOCTYPE html><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta charset=\"utf-8\" /><title></title></head><body><input type=\"text\"><input /></body></html>"); wbMap.Dock = DockStyle.Fill; return wbMap; } private void WbMap_FrameLoadEnd(object sender, FrameLoadEndEventArgs e) { Console.WriteLine("QGIS_WbMap_FrameLoadEnd"); }
解决方案
1 全局开启CefSharp触摸支持
在应用入口Program.cs中初始化Cef时添加触摸相关配置,避免触摸事件被默认拦截:
[STAThread] static void Main() { CefSettings cefSettings = new CefSettings(); // 显式开启触摸事件支持 cefSettings.TouchEventsEnabled = CefState.Enabled; Cef.Initialize(cefSettings); Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); }
2 拦截系统触摸消息主动同步焦点
重写窗体的WndProc方法,直接监听系统级触摸消息,不需要依赖CefSharp控件暴露的点击类事件,只要触摸点落在浏览器控件范围内就主动同步焦点:
// 触摸系统消息ID private const int WM_TOUCH = 0x0240; protected override void WndProc(ref Message m) { base.WndProc(ref m); if (m.Msg == WM_TOUCH) { // 解析触摸点屏幕坐标 Point screenTouchPos = new Point(m.LParam.ToInt32()); // 转换为窗体客户区坐标 Point clientTouchPos = PointToClient(screenTouchPos); // 判断是否点击到浏览器控件区域 if (wbMap.Bounds.Contains(clientTouchPos) && !wbMap.Focused) { // 先设置WinForm层面控件焦点 wbMap.Focus(); // 再同步设置Chromium内核层面的输入焦点 if (wbMap.IsBrowserInitialized) { wbMap.GetBrowser().GetHost().SetFocus(true); } } } }
以上方案兼容所有Win10/11版本的Surface设备,不会干扰浏览器外其他控件的操作逻辑,完全符合业务要求。如果使用的CefSharp版本低于75,只需要删掉内核焦点设置的代码即可正常运行。
内容的提问来源于stack exchange,提问作者semmelbrot

