You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

WPF触摸键盘异常:点击非文本框控件后需两次点击文本框才弹出

解决WPF Win10平板模式下触摸键盘闪显问题

哥们,我之前也踩过这个WPF和Win10触摸键盘的坑!你说的这个情况确实是WPF内部的触摸键盘状态计数出了问题——当焦点从TextBox这类可编辑控件切换到按钮、InkCanvas这类非编辑控件时,框架可能错误地多执行了一次「关闭键盘」的计数操作,导致后续再点击TextBox时,第一次点击只是抵消了错误的计数,第二次才会真正触发键盘弹出。下面给你两个靠谱的解决方案:

方案1:手动接管触摸键盘状态(最稳定)

绕过WPF的原生计数逻辑,直接通过系统API控制触摸键盘的显示和隐藏,彻底避免计数异常。

首先在你的MainWindow类中添加必要的API声明和事件监听:

using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

public partial class MainWindow : Window
{
    // 系统API声明,用于查找和控制触摸键盘窗口
    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int SW_SHOW = 5;
    private const int SW_HIDE = 0;

    public MainWindow()
    {
        InitializeComponent();
        // 给所有TextBox绑定焦点获取事件
        foreach (var textBox in FindVisualChildren<TextBox>(this))
        {
            textBox.GotFocus += OnTextBoxGotFocus;
        }
        // 给所有非TextBox的可聚焦控件绑定焦点获取事件
        foreach (var control in FindVisualChildren<Control>(this).Where(c => !(c is TextBox)))
        {
            control.GotFocus += OnNonTextBoxGotFocus;
        }
    }

    private void OnTextBoxGotFocus(object sender, RoutedEventArgs e)
    {
        // 显示触摸键盘
        var keyboardHandle = FindWindow("IPTip_Main_Window", null);
        if (keyboardHandle != IntPtr.Zero)
        {
            ShowWindow(keyboardHandle, SW_SHOW);
        }
    }

    private void OnNonTextBoxGotFocus(object sender, RoutedEventArgs e)
    {
        // 隐藏触摸键盘
        var keyboardHandle = FindWindow("IPTip_Main_Window", null);
        if (keyboardHandle != IntPtr.Zero)
        {
            ShowWindow(keyboardHandle, SW_HIDE);
        }
    }

    // 遍历VisualTree查找所有指定类型的子控件
    private IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
    {
        if (depObj == null) yield break;
        
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);
            if (child is T target)
            {
                yield return target;
            }
            // 递归查找子控件的子控件
            foreach (var subChild in FindVisualChildren<T>(child))
            {
                yield return subChild;
            }
        }
    }
}

方案2:修复WPF内部计数问题(轻量但依赖框架行为)

如果不想完全接管键盘控制,可以尝试在焦点切换时重置WPF的键盘状态,修正计数异常:

public partial class MainWindow : Window
{
    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int SW_HIDE = 0;

    public MainWindow()
    {
        InitializeComponent();
        foreach (var textBox in FindVisualChildren<TextBox>(this))
        {
            textBox.LostFocus += OnTextBoxLostFocus;
        }
    }

    private void OnTextBoxLostFocus(object sender, RoutedEventArgs e)
    {
        // 当焦点离开TextBox且新焦点不是另一个TextBox时,强制重置键盘状态
        if (!(Keyboard.FocusedElement is TextBox))
        {
            var keyboardHandle = FindWindow("IPTip_Main_Window", null);
            if (keyboardHandle != IntPtr.Zero)
            {
                ShowWindow(keyboardHandle, SW_HIDE);
            }
            // 额外触发一次WPF的焦点同步,修复计数问题
            Keyboard.Focus(null);
        }
    }

    // 同样需要上面的FindVisualChildren方法
}

注意事项

  • 仅在平板模式下测试,桌面模式触摸键盘默认不会自动弹出
  • 如果你的应用里有其他可编辑控件(比如RichTextBox),记得把它们也加入到TextBox的事件监听逻辑中
  • 避免直接多次启动TabTip.exe,否则会生成多个键盘实例,加剧状态混乱

内容的提问来源于stack exchange,提问作者m0n5ter

火山引擎 最新活动