【问题标题】:How can I make CefSharp.Wpf.HwndHost open the touch keyboard如何让 CefSharp.Wpf.HwndHost 打开触摸键盘
【发布时间】:2022-06-15 04:34:57
【问题描述】:

我们过去曾使用 CefSharp.Wpf + Touch keyboard sample 在包装 CEF Sharp 的 WPF Kiosk 上打开平板电脑键盘。由于它使用CefSharp.Wpf - 我们在某些设备上面临着相对常见的 GPU 渲染问题,以及对性能的失望。切换到 CefSharp.Wpf.HwndHost 可以解决我们的 GPU 问题,但不能可靠地触发触摸键盘。

如何触发 Windows 10 触控键盘? (OSK / TabTip.exe)

不起作用的事情:

  • disable-usb-keyboard-detect 本身不会产生可靠的 SIP 激活
  • CefSharp.Wpf.HwndHost 本身不会产生可靠的 SIP 激活,但有时会起作用并且会为数字等产生正确的 SIP 类型。
  • CefSharp.Wpf 不产生任何 SIP 激活(在框中),并在许多机器上产生 GPU 工件和性能问题
  • CefSharp.Wpf + Touch keyboard sample 确实产生可靠的 SIP 激活,但不控制 SIP 键盘类型(例如:<input type="number" 上未显示数字键盘)并且仍然存在 GPU 和性能问题

【问题讨论】:

    标签: c# wpf cefsharp on-screen-keyboard


    【解决方案1】:

    您可以使用相同的IInputPane2 界面来触发触摸键盘。 CefSharp.Wpf.HwndHost 需要使用 IRenderProcessMessageHandler 来接收焦点更改,因为 VirtualKeyboardRequested 事件特定于 CefSharp.Wpf

    由于IRenderProcessMessageHandler.OnFocusedNodeChanged 事件以相当高的频率触发,并且在不需要 OSK 的情况下,需要对事件进行过滤和去抖动。使用 Rx.Net 做到这一点很简单。 A full example can be found on Github.

    Project:

    • 将 TFM 设置为 net6.0-windows10.0.19041
    • 添加<PackageReference Include="CefSharp.Wpf.HwndHost" Version="98.1.210" />
    • 添加<PackageReference Include="System.Reactive" Version="5.0.0" />

    Program.cs:

    Cef.EnableHighDPISupport();
    
    var settings = new CefSettings();
    CefSharpSettings.FocusedNodeChangedEnabled = true;
    settings.CefCommandLineArgs.Add("disable-usb-keyboard-detect", "1");
    Cef.Initialize(settings);
    

    MainWindow.xaml.cs:

    public partial class MainWindow : Window
    {
        private Lazy<(IInputPaneInterop ipi, IInputPane2 ip)> sip;
    
        public MainWindow()
        {
            InitializeComponent();
    
            sip = new Lazy<(IInputPaneInterop ipi, IInputPane2 ip)>(() =>
            {
                var hwnd = new WindowInteropHelper(this).Handle;
                var ipi = InputPane.As<IInputPaneInterop>();
                var ip = ipi.GetForWindow(hwnd, typeof(IInputPane2).GUID);
                return (ipi, ip);
            });
    
            var oskSubject = new Subject<bool>();
            cwb.RenderProcessMessageHandler = new OskRenderProcessMessageHandler(oskSubject.OnNext);
            oskSubject
                .Throttle(TimeSpan.FromMilliseconds(200))
                .ObserveOn(SynchronizationContext.Current ?? throw new InvalidOperationException("No syncctx"))
                .Subscribe(PopOsk);
        }
    
        protected override void OnClosed(EventArgs e)
        {
            if (sip.IsValueCreated)
            {
                var (ipi, ip) = sip.Value;
                Marshal.FinalReleaseComObject(ip);
                Marshal.FinalReleaseComObject(ipi);
            }
            base.OnClosed(e);
        }
    
        private void PopOsk(bool shouldShow)
        {
            var (_, ip) = sip.Value;
            if (shouldShow)
            {
                Debug.WriteLine($"Showing SIP");
                ip.TryShow();
            }
            else
            {
                Debug.WriteLine($"Hiding SIP");
                ip.TryHide();
            }
        }
    }
    
    internal class OskRenderProcessMessageHandler : IRenderProcessMessageHandler
    {
        private readonly Action<bool> SetOsk;
    
        public OskRenderProcessMessageHandler(Action<bool> popOsk)
        {
            this.SetOsk = popOsk;
        }
    
        public void OnContextCreated(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame)
        {
            // nop
        }
    
        public void OnContextReleased(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame)
        {
            // nop
        }
    
        public void OnFocusedNodeChanged(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IDomNode? node)
        {
            SetOsk(node != null && "input".Equals(node.TagName, StringComparison.InvariantCultureIgnoreCase));
        }
    
        public void OnUncaughtException(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, JavascriptException exception)
        {
            // nop
        }
    }
    
    [ComImport, Guid("75CF2C57-9195-4931-8332-F0B409E916AF"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IInputPaneInterop
    {
        void _VtblGap1_3();
    
        IInputPane2 GetForWindow([In] IntPtr appWindow, [In] ref Guid riid);
    }
    
    [ComImport, Guid("8A6B3F26-7090-4793-944C-C3F2CDE26276"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IInputPane2
    {
        void _VtblGap1_3();
    
        bool TryShow();
        bool TryHide();
    }
    

    为了说明的目的省略了错误检查。

    一般注意事项:

    1. 仅在 Windows 10 Enterprise 20H2 (10.0.19042) 及更高版本中进行了测试。未在 W11 上测试
    2. 不应存在硬件键盘(或 GPIO 应指示键盘已禁用 - 例如:Lenovo Yoga 处于平板电脑模式)
    3. 系统必须配置为在桌面模式下使用触摸键盘(要求不明确)
    4. 应用启动时的行为因触摸输入设备和系统状态而异
    5. 行为因BorderStyle 而异
    6. 触发手势必须源自触摸 - 不能使用鼠标进行测试

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-03
      • 2019-12-01
      • 1970-01-01
      • 2016-04-18
      • 2011-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多