簡體   English   中英

如何在Windows 10平板電腦中獲得“觸摸屏鍵盤”以停止在WPF應用程序中打開兩次?

[英]How can I get the “touchscreen keyboard” in a Windows 10 tablet to stop opening twice in our WPF app?

請注意,我已經花了很多時間搜索在線帖子,包括SO方面的信息,但到目前為止沒有成功。

問題出在每當有人單擊文本框時,由於Windows 10 Touch Keyboard and Handwriting Panel Service原因,觸摸屏鍵盤已開始自動打開,而在Windows 8.1之前,鍵盤的打開僅是由於來自Structures資產中的C#API調用管理(SAM)應用。 因此,在Windows 10中,每當有人單擊文本框時,都會兩次打開虛擬鍵盤-一次是由於SAM C#API調用,一次是由於Touch Keyboard and Handwriting Panel Service

請注意,我們已經嘗試禁用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中的行為相同? 我可以在OS設置上更改某些配置值,還是需要在注冊表中進行某些更改? 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/zh-cn/windows/uwp/porting/

從WPF和Silverlight移至WinRT

相關話題

最后使用的是C#內置的自定義WPF軟件鍵盤,而不是Windows“觸摸鍵盤和手寫面板服務”觸摸屏鍵盤和屏幕鍵盤。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM