C#WPF Windows 10(1803)TouchKeyboard不可靠问题(Prism ClickOnce)

问题描述 投票:3回答:3

我正在尝试修复我们在C#Prism WPF应用程序中使用Onscreenkeyboard的问题,该应用程序的目标是通过ClickOnce部署的.Net 4.5.2,在Windows 10版本1803上运行。

我们的应用程序有一个触发事件的TimeOut函数,在这种情况下,我想要切换键盘的可见性,或者如果它打开则关闭它。下面的类中的Close()函数有时不起作用,我在日志中看到(未在下面的代码中显示)应用程序找到句柄(> 0)并在其上发出关闭命令但键盘没有关闭。

这个问题与下面提到和链接的2个问题有关!但我不能发表评论所以我不得不提出一个新问题。

Determine if Windows 10 Touch Keyboard is Visible or Hidden提出之前,我们在v1709中有一种工作解决方案

现在我正在尝试实现该问题中的最新帖子,以便能够在超时或应用关闭事件时切换键盘的可见性,如果它检测到键盘处于打开状态,我正在尝试将其与您在下面看到的键盘类组合我遇到了麻烦。

所以我们有上面的帖子和Show touch keyboard (TabTip.exe) in Windows 10 Anniversary edition组合的触摸键盘类:

static class TouchKeyboard
{


    private static void StartTabTip()
    {
        var p = Process.Start(@"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
        int handle = 0;
        while ((handle = NativeMethods.FindWindow("IPTIP_Main_Window", "")) <= 0)
        {
            Thread.Sleep(100);
        }
    }

    public static void ToggleVisibility()
    {
        var type = Type.GetTypeFromCLSID(Guid.Parse("4ce576fa-83dc-4F88-951c-9d0782b4e376"));
        var instance = (ITipInvocation)Activator.CreateInstance(type);
        instance.Toggle(NativeMethods.GetDesktopWindow());
        Marshal.ReleaseComObject(instance);
    }

    public static void Show()
    {
        int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
        if (handle <= 0) // nothing found
        {
            StartTabTip();                
            Thread.Sleep(100);                
        }
        // on some devices starting TabTip don't show keyboard, on some does  ¯\_(ツ)_/¯
        if (!IsOpen())
        {
            ToggleVisibility();
        }
    }

    public static void Hide()
    {
        if (IsOpen())
        {
            ToggleVisibility();
        }
    }        


    public static bool Close()
    {
        // find it
        int handle = NativeMethods.FindWindow("IPTIP_Main_Window", "");
        bool active = handle > 0;
        if (active)
        {
            // don't check style - just close
            NativeMethods.SendMessage(handle, NativeMethods.WM_SYSCOMMAND, NativeMethods.SC_CLOSE, 0);
        }
        return active;
    }

   public static bool GetIsOpen()
   {
      return GetIsOpen1709() ?? GetIsOpenLegacy();
   }

private static bool? GetIsOpen1709()
{
    var parent = IntPtr.Zero;
    for (;;)
    {
        parent = FindWindowEx(IntPtr.Zero, parent, WindowParentClass1709);
        if (parent == IntPtr.Zero)
            return null; // no more windows, keyboard state is unknown

        // if it's a child of a WindowParentClass1709 window - the keyboard is open
        var wnd = FindWindowEx(parent, IntPtr.Zero, WindowClass1709, WindowCaption1709);
        if (wnd != IntPtr.Zero)
            return true;
    }
}

private static bool GetIsOpenLegacy()
{
    var wnd = FindWindowEx(IntPtr.Zero, IntPtr.Zero, WindowClass);
    if (wnd == IntPtr.Zero)
        return false;

    var style = GetWindowStyle(wnd);
    return style.HasFlag(WindowStyle.Visible)
        && !style.HasFlag(WindowStyle.Disabled);
}

private const string WindowClass = "IPTip_Main_Window";
private const string WindowParentClass1709 = "ApplicationFrameWindow";
private const string WindowClass1709 = "Windows.UI.Core.CoreWindow";
private const string WindowCaption1709 = "Microsoft Text Input Application";

private enum WindowStyle : uint
{
    Disabled = 0x08000000,
    Visible = 0x10000000,
}

private static WindowStyle GetWindowStyle(IntPtr wnd)
{
    return (WindowStyle)GetWindowLong(wnd, -16);
}

[DllImport("user32.dll", SetLastError = false)]
private static extern IntPtr FindWindowEx(IntPtr parent, IntPtr after, string className, string title = null);

[DllImport("user32.dll", SetLastError = false)]
private static extern uint GetWindowLong(IntPtr wnd, int index);

这在1709年运行得相当不错,但在1803年不再有了,并且新客户要求在超时时关闭键盘。所以现在我已将该stackoverflow问题的最新帖子中的代码作为函数添加到上面的类中(我不认为这是正确的方法):

   static class TouchKeyboard
   {
    ///Above code omitted

    public static bool GetIsOpen1803()
    {
        // do this once:
        var brokerClass = new ImmersiveShellBroker();
        var broker = (IImmersiveShellBroker)brokerClass;
        var ihm = broker.GetInputHostManagerBroker();
        Marshal.ReleaseComObject(broker);

        // now ihm reference can be cached and used later:
        Rect rect;
        DisplayMode mode;
        ihm.GetIhmLocation(out rect, out mode);

    }

    [ComImport, Guid("228826af-02e1-4226-a9e0-99a855e455a6")]
    class ImmersiveShellBroker
    {
    }

    [ComImport, Guid("9767060c-9476-42e2-8f7b-2f10fd13765c")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IImmersiveShellBroker
    {
        void Dummy();
        IInputHostManagerBroker GetInputHostManagerBroker();
    }

    [ComImport, Guid("2166ee67-71df-4476-8394-0ced2ed05274")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IInputHostManagerBroker
    {
        void GetIhmLocation(out Rect rect, out DisplayMode mode);
    }

    [StructLayout(LayoutKind.Sequential)]
    struct Rect
    {
        public int Left, Top, Right, Bottom;
    }

    enum DisplayMode
    {
        NotSupported = 0,
        Floating = 2,
        Docked = 3,
    }
    }

我认为我正在实现它不正确,因为它不能一直工作,当我从上面提到的超时事件调用GetIsOpen1803时,它有时会返回矩形为0,0,0,0而我看到键盘已启动。在代码中它在评论中说“只执行一次”和“可以缓存并稍后使用”,但我似乎无法掌握如何在我们拥有的Touchkeyboard类中执行此操作。

到目前为止,如果键盘在应用程序中的任何位置都在屏幕上并且能够关闭它,那么GetIsOpen方法可以可靠地返回。即使终止所有tabtip进程并不总是关闭键盘!唯一有效的方法是终止服务,但由于需要提升权限,我无法做到这一点。

那么,绝对让我的一天找到一种可靠的方式来查看键盘是否为所有Windows 10版本打开以及关闭它或切换它的可见性(如果有的话)的可靠方式。

任何帮助将不胜感激!

c# wpf windows-10 prism clickonce
3个回答
0
投票

你能尝试更新我的库变种 - https://github.com/AlexeiScherbakov/osklib

您可以使用OnScreenKeyboardWatcher / DispatcherOnScreenKeyboardWatcher / WinFormsKeyboardWatcher:1。检测当前键盘状态2.观察键盘打开的时间(超时)

我已经在旧版本(1709,1703和1607)上测试了这段代码。在我的1803(版本17134)上它也有效


0
投票

我们无法通过提供的解决方案使键盘可靠地工作。对我们有用的唯一(且简单)解决方案是升级到.Net Framework 4.7.2并使用它升级我们解决方案中的所有Nuget包。几个月前我们无法使用4.7.2,因为ClickOnce没有内置的正确引用,这在最近的更新中得到了解决。

在我们升级到4.7.2后,我们从应用程序中删除了所有与键盘相关的代码,现在一切都按预期工作(在平板电脑模式下,这是我们想要的),没有任何代码,我可能会添加!所以我没有发布任何代码的原因仅仅是因为没有任何代码!


-2
投票

正如Tim Bijnens所说,当他开始这个线程时,键盘切换(ITipInvocation.Toggle())并不总是调出键盘。

在Win10 Ver 1803,DesktopMode中,没有可靠的方法 切换“触摸键盘”打开|关闭[ITipInvocation.Toggle()]; 也不能可靠地发现它是否“向上”(在屏幕上) [IFrameworkInputPane.Location()];两个例程都随机失败。

相反,请确保“TabTIP.EXE”和“.... InputApp.EXE” 仅在键盘“向上”(屏幕上)时运行;看到: https://stackoverflow.com/a/51376030/5732431

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