如何在Windows 10平板电脑中使用“触摸屏键盘”在我们的WPF应用程序中停止打开两次?

问题描述 投票:0回答:1

请注意,我花了很多时间搜索在线帖子,包括SO,但到目前为止都没有成功。

问题在于触摸屏键盘由于Windows 10 Touch Keyboard and Handwriting Panel Service自动打开,只要有人点击文本框,而在使用Windows 8.1之前,键盘只是由于我们的结构资产管理(SAM)应用程序中的C#API调用而打开。因此,对于Windows 10,只要有人点击文本框,虚拟键盘就会被打开两次 - 一次是因为SAM C#API调用,一次是由于Touch Keyboard and Handwriting Panel Service

请注意,我们已尝试禁用Touch Keyboard and Handwriting Panel Service,但这会导致触摸屏键盘根本不显示。

通常情况下,让操作系统使用Touch Keyboard and Handwriting Panel Service打开这个触摸屏键盘会很好,但问题是我们有时需要显示触摸屏键盘而其他时候只显示一个数字键盘,所以只依靠Windows服务不是一个选项。

以下是在Windows 8.1中成功控制键盘的类:

using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Input;

namespace Cpr.Apps.Sam.Controls
{
    /// <summary>
    /// Shows or hides the touch keyboard on tablets.
    /// </summary>
    public class TouchKeyboard
    {
        /// <summary>
        /// The touch keyboard app's file path.
        /// </summary>
        private static string _touchKeyboardAppFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonProgramFiles), @"Microsoft Shared\ink\TabTip.exe");

        /// <summary>
        /// Set to true if the app is currently running on a touch device and false otherwise.
        /// </summary>
        private static bool _isTouchScreen = Tablet.TabletDevices.Cast<TabletDevice>().Any(tabletDevice => tabletDevice.Type == TabletDeviceType.Touch);

        /// <summary>
        /// The keyboard visible flag.
        /// </summary>
        /// <remarks>
        /// This flag only keeps track of the keyboard's visibility if it was set using this class.
        /// </remarks>
        private static bool _isKeyboardVisible;

        /// <summary>
        /// The delay after which the keyboard will be hidden, in seconds.
        /// </summary>
        /// <remarks>
        /// The keyboard is not hidden immediately when the associated input field loses the keyboard focus, so that it will
        /// not flicker if another input field with this behavior obtains the keyboard focus immediately afterwards.
        /// </remarks>
        private const double KEYBOARD_HIDE_DELAY = 0.25;

        /// <summary>
        /// The number of milliseconds per second. Used for time conversions.
        /// </summary>
        private const long MILLISECONDS_PER_SECOND = 1000;

        /// <summary>
        /// True if the current device has a touch screen and false otherwise.
        /// </summary>
        public static bool IsTouchScreen
        {
            get { return _isTouchScreen; }
        }

        /// <summary>
        /// Shows the touch keyboard if the app is running on a touch device.
        /// </summary>
        /// <remarks>
        /// This method does nothing if the app is not currently running on a touch device.
        /// </remarks>
        public static void Show()
        {
            // check if the app is running on a touch device
            if (_isTouchScreen && _touchKeyboardAppFilePath != null)
            {
                try
                {
                    // launch the touch keyboard app
                    Process.Start(_touchKeyboardAppFilePath);

                    // set the keyboard visible flag
                    _isKeyboardVisible = true;
                }
                catch (Exception)
                {
                    // do nothing
                }
            }
        }

        /// <summary>
        /// Hides the touch keyboard if the app is running on a touch device.
        /// </summary>
        /// <remarks>
        /// This method does nothing if the app is not currently running on a touch device.
        /// </remarks>
        public static void Hide()
        {
            // check if the app is running on a touch device
            if (_isTouchScreen)
            {
                // reset the keyboard visible flag
                _isKeyboardVisible = false;

                // hide the keyboard after a delay so that if another input field with this behavior obtains the focus immediately,
                // the keyboard will not flicker
                Timer timer = null;
                timer = new Timer((obj) =>
                {
                    // check if the keyboard should still be hidden
                    if (!_isKeyboardVisible)
                    {
                        // check if the keyboard is visible
                        var touchKeyboardWindowHandle = FindWindow("IPTip_Main_Window", null);
                        if (touchKeyboardWindowHandle != _nullPointer)
                        {
                            // hide the keyboard
                            SendMessage(touchKeyboardWindowHandle, WM_SYSCOMMAND, SC_CLOSE, _nullPointer);
                        }
                    }

                    // release the timer
                    timer.Dispose();
                }, null, (long)(KEYBOARD_HIDE_DELAY * MILLISECONDS_PER_SECOND), Timeout.Infinite);
            }
        }

        // Win32 null pointer parameter
        private static IntPtr _nullPointer = new IntPtr(0);

        // Win32 command from the Window menu
        private const uint WM_SYSCOMMAND = 0x0112;

        // Win32 command to close a window
        private static IntPtr SC_CLOSE = new IntPtr(0xF060);

        // Win32 API to get a window reference
        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern IntPtr FindWindow(string sClassName, string sAppName);

        // Win32 API to send a message to a window
        [DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true)]
        private static extern IntPtr SendMessage(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
    }
}

和:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using Cpr.Apps.Sam.Controls;

namespace Cpr.Apps.Sam.Styles.Behaviors
{
    /// <summary>
    /// Behavior that shows the touch keyboard (on tablets only) when the associated control gets the keyboard focus.
    /// </summary>
    public class ControlShowTouchKeyboardOnFocusBehavior : Behavior<Control>
    {
        protected override void OnAttached()
        {
            base.OnAttached();

            // add the event handlers
            WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "GotKeyboardFocus", OnGotKeyboardFocus);
            WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "LostKeyboardFocus", OnLostKeyboardFocus);
        }

        /// <summary>
        /// Called when the associated control receives the keyboard focus.
        /// </summary>
        /// <param name="sender">The object triggering this event.</param>
        /// <param name="e">The event parameters.</param>
        private void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            // show the touch keyboard
            TouchKeyboard.Show();
            var textBox = sender as TextBox;
            if (textBox != null)
                textBox.SelectionStart = Math.Max(0, textBox.Text.Length);  //Move the caret to the end of the text in the text box.
        }

        /// <summary>
        /// Called when the associated control loses the keyboard focus.
        /// </summary>
        /// <param name="sender">The object triggering this event.</param>
        /// <param name="e">The event parameters.</param>
        private void OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            // hide the touch keyboard
            TouchKeyboard.Hide();
        }
    }
}

基本上,我的问题是,如何在Windows 10中使触摸屏键盘的行为与在Windows 8.1中的行为相同?我可以在操作系统设置上更改一些配置值,还是需要在注册表中更改某些内容? Windows 8.1和Windows 10中的Touch Panel and Handwriting Service有什么区别? TIA。

更新:

请注意,我已经探索过使用“屏幕键盘”,我认为它基于COM而不是较新的“触摸屏键盘”,但这没有帮助,因为最终因为这个COM键盘需要管理员权限来关闭或最小化它。这就是我用“屏幕键盘”尝试的方法:

using System;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using Cpr.Apps.Sam.Controls;

namespace Cpr.Apps.Sam.Styles.Behaviors
{
    /// <summary>
    /// Behavior that shows the touch keyboard (on tablets only) when the associated control gets the keyboard focus.
    /// </summary>
    public class ControlShowTouchKeyboardOnFocusBehavior : Behavior<Control>
    {
        [DllImport("User32")]
        private static extern int ShowWindow(int hwnd, int nCmdShow);

        private const int SW_HIDE = 0;
        private const int SW_RESTORE = 9;
        private int hWnd;
        private readonly string _USB = "USB";
        private readonly string _keyboard = @"osk.exe";
        private Process _keyboardProcess = null;
        private ProcessStartInfo _startInfo = null;

        protected override void OnAttached()
        {
            base.OnAttached();

            // add the event handlers
            WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "GotKeyboardFocus", OnGotKeyboardFocus);
            WeakEventManager<Control, KeyboardFocusChangedEventArgs>.AddHandler(AssociatedObject, "LostKeyboardFocus", OnLostKeyboardFocus);
        }

        private bool GetKeyboardPresent()
        {
            bool flag = false;
            foreach (ManagementBaseObject managementBaseObject in new ManagementObjectSearcher("Select * from Win32_Keyboard").Get())
            {
                foreach (PropertyData property in managementBaseObject.Properties)
                {
                    if (Convert.ToString(property.Value).Contains(this._USB))
                    {
                        flag = true;
                        break;
                    }
                }
            }

            return flag;
        }

        /// <summary>
        /// Called when the associated control receives the keyboard focus.
        /// </summary>
        /// <param name="sender">The object triggering this event.</param>
        /// <param name="e">The event parameters.</param>
        private void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            // show the touch keyboard
            // TouchKeyboard call here not needed in Windows 10 because of “Touch Keyboard and Handwriting Panel Service” causes virtual keyboard to show up twice.  Use on screen keyboard instead. 
            //TouchKeyboard.Show();
            if (!this.GetKeyboardPresent())
            {
                //_keyboardProcess.StartInfo.WindowStyle = ProcessWindowStyle.Maximized;
                Process[] pocesses = Process.GetProcessesByName(_keyboard);

                if (pocesses.Any())
                {
                    foreach (var proc in pocesses)
                    {
                        hWnd = (int) proc.MainWindowHandle;
                        ShowWindow(hWnd, SW_RESTORE);
                    }
                }
                else
                {
                    _startInfo = new ProcessStartInfo(_keyboard);
                    _keyboardProcess = new Process
                    {
                        EnableRaisingEvents = true,
                        StartInfo = _startInfo
                    };
                    _keyboardProcess.Exited += new EventHandler(ProcessExited);
                    //Don't need this because it is for parent process: AppDomain.CurrentDomain.ProcessExit += (a, b) => _keyboardProcess.Kill();
                    _keyboardProcess.Start();
                }

            }

            var textBox = sender as TextBox;
            if (textBox != null)
                textBox.SelectionStart = Math.Max(0, textBox.Text.Length);  //Move the caret to the end of the text in the text box.
        }

        /// <summary>
        /// Called when the associated control loses the keyboard focus.
        /// </summary>
        /// <param name="sender">The object triggering this event.</param>
        /// <param name="e">The event parameters.</param>
        private void OnLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            // hide the touch keyboard
            // TouchKeyboard call here not needed in Windows 10 because of “Touch Keyboard and Handwriting Panel Service” causes virtual keyboard to show up twice.  Use on screen keyboard instead. 
            //TouchKeyboard.Hide();
            if (!GetKeyboardPresent() && _keyboardProcess != null)
            {
                //Keyboard doesn't minimize if I call Kill() or SW_HIDE, and instead this simply causes the textbox to lose focus so commented out this code
                //Process[] pocesses = Process.GetProcessesByName("osk");

                //for (int i = 0; i < pocesses.Count(); i++)
                //{
                //    var proc = pocesses[i];
                //    proc.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
                    //hWnd = (int)proc.MainWindowHandle;
                    //ShowWindow(hWnd, SW_HIDE);
                //}

                //Task.Delay(500);
            }
        }

        private void ProcessExited(object sender, System.EventArgs e)
        {
            Debug.WriteLine("Exited _keyboardProcess");
            _keyboardProcess = null;
        }
    }
}

更新2:

看起来我可能需要将我的应用程序从WPF移植到WinRT以使其在Windows 10上运行:请参阅https://docs.microsoft.com/en-us/windows/uwp/porting/其中所说的内容

从WPF和Silverlight迁移到WinRT

相关话题

c# wpf windows-10 windows-8.1
1个回答
0
投票

使用内置于C#的自定义WPF软件键盘而不是Windows“触摸键盘和手写面板服务”触摸屏键盘和屏幕键盘结束。

© www.soinside.com 2019 - 2024. All rights reserved.