【问题标题】:Automatically pop up tablet touch keyboard on WinForms input focusWinForms输入焦点上自动弹出平板触摸键盘
【发布时间】:2016-03-23 13:43:59
【问题描述】:

当我在平板电脑模式下在 Windows 10 上运行 WinForms(或 Delphi,见最后)应用程序时,当输入框获得焦点时,触摸键盘不会pop up automatically

我相信这应该自动发生,无需任何额外的代码/设置。


为了测试,我有一个最简单的 VS 2015 WinForms 桌面应用程序,只有一个 TextBox control

它只是由 Visual Studio 创建的默认 Windows 窗体应用程序 C# 项目。未添加代码,未更改属性。通过从 Toolbox 中删除添加了 TextBox(同样没有更改任何属性):

this.textBox1 = new System.Windows.Forms.TextBox();
this.textBox1.Location = new System.Drawing.Point(64, 27);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(100, 20);
this.textBox1.TabIndex = 0;

验证我的假设,即弹出窗口应该是自动的:

  • 我尝试在 Windows 10 上运行 Windows XP 版本的notepad.exe。它会自动弹出触摸键盘。我怀疑 Windows XP 是否明确支持触摸键盘。

  • 我也尝试过一些古老的 MFC 应用程序(例如 2005 年的 FileZilla 2.2.15)。它还会在其所有输入框中弹出触摸键盘。同样,我很确定,MFC 也没有明确支持触摸键盘。

  • 对于基于 wxWidgets 构建的应用程序(例如 FileZilla 3.x)也是如此。


WinForms 中似乎有一些问题阻止了自动弹出。有趣的是,自动弹出窗口有效:

  • 用于(可编辑的)组合框(ComboBoxDropDownStyle = DropDown
  • 用于密码模式下的文本框 (TextBox.PasswordChar)
  • 用于富文本框 (RichTextBox)
  • 当输入框有焦点时,硬件键盘被“移除”(我通过在联想 Yoga 笔记本上翻转屏幕进行测试),但之后再也没有。

我已经通过运行TabTip.exe 看到了有关显式弹出窗口的所有提示。例如:

大多数“解决方案”都提供这样的代码:

var progFiles = @"C:\Program Files\Common Files\Microsoft Shared\ink";
var keyboardPath = Path.Combine(progFiles, "TabTip.exe");
this.keyboardProc = Process.Start(keyboardPath);

但我不敢相信这可能是“官方”方式。如果没有别的原因,那么因为没有干净的方法来隐藏通过运行TabTip.exe 打开的键盘(解决方案包括诸如终止进程或发送 Esc 键等黑客攻击)。

实际上,上述 hack 在 Windows 10 周年更新中似乎不再适用:


有趣的是,我在 Delphi/C++ Builder/VCL 应用程序中看到了相同的行为。键盘不会弹出编辑框 (TEdit)。它确实会弹出组合框 (TComboBox) 和密码模式下的编辑框 (PasswordChar)。有趣的是,TRichEdit 不是,与 .NET RichTextBox 有什么显着差异,这可能值得研究。

这个(未回答的)问题描述了一个相同的行为:
Application written Delphi XE8 touch in edit boxes keyboard not appear in Windows 10

【问题讨论】:

    标签: c# windows winforms delphi touch


    【解决方案1】:

    根本原因似乎是 Winforms 的 textBox 不是 AutomationElement,而其余提到的控件(ComboBoxes 等)是。

    引用 Markus von und zu Heber 的accepted answer here

    我们在文章“Automatic Touch Keyboard for TextBoxes in WPF Applications on Windows 8+”中找到了它,但它也很好用(甚至 更容易!)对于winforms。谢谢你,德米特里·莱亚林!

    1. 将 UIAutomationClient.dll 的引用插入到您的项目中

    2. 在应用程序主窗口的表单加载处理程序中,插入以下代码:

      var asForm = System.Windows.Automation.AutomationElement.FromHandle(this.Handle);
      

    【讨论】:

    • 调用AutomationElement.FromHandle(handle) 确实达到了我想要的效果。但这只是一个副作用。您实际上可以将任何内容传递给该方法,包括像(IntPtr)(-1) 这样的无效句柄。重要的是方法实现使用了一个内部类UiaCoreApi。它的类初始化器有一些魔力,它使编辑框的触摸键盘弹出。但它也做了无数其他事情并加载了许多不相关的程序集,所有这些都有我不愿意盲目使用的未知副作用。
    • 谢谢。也许有人可以基于这些信息。我花了很多时间试图找出真正的触发器是什么,但我没有成功。
    • 实际上,如果由应用程序实现,触摸键盘可能实际上可以利用 UI 自动化。这就是 UiaCoreApi 初始化程序所做的。也许有人可以证实这一点。
    • 感谢您深入了解此 Martin。我最终在这里使用了这个解决方案。这些是让我发疯的事情,我会花费大量时间来追查这些事情。我希望我能为这篇文章 +10。
    【解决方案2】:

    我已经走这条路好几次了,但只能实现taptip.exe 选项。然后通过终止进程来关闭窗口。我还发现,通过一些注册表黑客,如果您愿意,您可以让键盘默认为手写面板。但这仅适用于Win8,而在Win10中则失败。以下是我所做的,以防其他人觉得这很有用:

    RegistryKey registryKey = Registry.CurrentUser.CreateSubKey("Software\\Microsoft\\TabletTip\\1.7");
    
    registryKey?.SetValue("KeyboardLayoutPreference", 0, RegistryValueKind.DWord);
    registryKey?.SetValue("LastUsedModalityWasHandwriting", 1, RegistryValueKind.DWord);
    
    Process.Start(@"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
    

    我需要感谢这篇文章的注册理念:Windows 8 Desktop App: Open tabtip.exe to secondary keyboard (for numeric textbox)

    【讨论】:

    • 感谢您的回答。但正如我已经评论过另一个答案:那么为什么它适用于 MFC/wxWidgets 或组合框?
    • @MartinPrikryl - 我无法对此发表评论,因为我从未使用过这些小部件。有人想有一种更简单的方法来弹出内置的屏幕键盘,但正如您所研究的那样,这似乎是不可能的。
    • @MartinPrikryl - 我偶然发现了这个答案,因为它可以在 Win10 中工作。我处于需要它在 Win10 中工作的情况,所以我找到了这个。我还没有尝试过,但看起来很受欢迎,以至于其他人已经成功了。 stackoverflow.com/questions/38774139/…
    【解决方案3】:

    据我所知,启动 osk.exetabtip.exe 几乎是完成这项工作的“标准”方式。到目前为止,我还没有找到“官方”解决方案。

    但是,如果是我这样做,我不会终止进程或发送键来尝试关闭键盘。相反,您可以在启动进程时获取窗口句柄,并使用它来最小化窗口并将其从任务栏中隐藏。

    这里有人得到窗口句柄只是为了关闭它,但它给了你这个想法:Show & hiding the Windows 8 on screen keyboard from WPF

    如果你需要我,请告诉我,我会看看是否有时间做一个完整的例子。

    【讨论】:

    • 感谢您的回答。那么为什么它适用于 MFC/wxWidgets 或组合框呢?
    【解决方案4】:

    正如Ofek Shilon's answer 所暗示的,触摸键盘似乎可以利用UI automation


    可以使用UIAutomationClient.dll 的 UI 自动化实现。

    为了将 UI 自动化神奇地注入应用程序,必须触发程序集内部类 UiaCoreApi 的类初始化器。

    On 可以实现这一点,例如通过调用似乎无操作:

    AutomationElement.FromHandle(IntPtr)(-1)
    

    另一种方法是显式实现自动化 UI。为此,为相应的输入控件实现 ITextProvider/IValueProvider 接口。

    要将接口的实现绑定到控件,请将WM_GETOBJECT window message 处理为lParam = RootObjectId

    有关实现示例,请参阅


    虽然有趣的是,触摸键盘开箱即用的控件(如组合框或密码编辑框,请参阅答案)不实现WM_GETOBJECT/RootObjectId。他们背后肯定有不同的机器。

    【讨论】:

      【解决方案5】:

      使用 RichTextBox 代替 TextBox 控件。 RichTextBox 支持触摸键盘,获得焦点时会自动弹出键盘。 (类似于组合框等其他输入控件)

      RichTextBox 也支持与 TextBox 相同的属性,因此在大多数情况下它应该是一个变化。 (两个控件都派生自 TextBoxBase)

      我注意到,如果触摸键盘在弹出后被关闭,您可能需要在控件上点按两次才能使其重新弹出。

      【讨论】:

      • 谢谢。这是一个有趣的观察结果,尽管不是一个可接受的解决方案。无论如何,我已将此信息添加到我的问题中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-20
      • 2014-11-18
      • 1970-01-01
      • 2012-08-10
      相关资源
      最近更新 更多