32

For our WPF application, when it runs on touch screen(Surface Pro .etc), the TextBox/PasswordBox control cannot show virtual keyboard when they get focused.

Any good way to implement this feature in WPF?


Updated:

what we want to achieve finally is something like that:

If user run app on PC, we don't care about this feature, which means whether user has physical keyboard, we do nothing just like normal WPF application running on PC.

If user run on Surface Pro, when he clicks the TextBox, the built-in virtual keyboard can show up, and which should be user-friendly, such as the keyboard would never cover up the input element.


Updated 2:

So, WPF cannot easily set some property to implement this feature? In my opinion, this feature should be built-in WPF, I don't understand why I cannot find an easy way to achieve.

  • Do you already have a virtual keyboard? If so, which one? If not, I understand, you need to take the focus to `TextBlock`, show the virtual keyboard. – Anatoliy Nikolaev Oct 05 '13 at 17:59
  • How are users without a physical keyboard going to enter text into your application? – Eric Brown Oct 06 '13 at 02:18
  • 2
    @AnatoliyNikolaev In this stage, we don't have a custom virtual keyboard and we think if WPF offers the feature, we can save a lot of work. –  Oct 06 '13 at 03:18
  • 1
    @EricBrown Hi, what we want to achieve finally is something like that: **If user run app on PC**, we don't care about this feature, which means whether user has physical keyboard, we do nothing just like normal WPF application running on PC. **If user run on Surface Pro**, when he clicks the `TextBox`, the built-in virtual keyboard can show up, and which should be user-friendly, such as the keyboard would never cover up the input element. –  Oct 06 '13 at 03:25
  • I'm still confused, then. There are more tablets than Surface Pro. Is the distinguishing characteristic the presence of a physical keyboard? – Eric Brown Oct 06 '13 at 05:31
  • @EricBrown No. Actually, we only target PC and Surface Pro, so when in the touch mode, we only consider Windows 8. If the Surface Pro connect the physical keyboard, then it's another problem, not this one. We just want to show the virtual keyboard built-in the Windows 8 OS when the input element got focused. –  Oct 06 '13 at 06:07
  • Let me be more explicit. How are you distinguishing between PC and Surface Pro? – Eric Brown Oct 06 '13 at 06:08
  • @EricBrown Sorry, we can't. In our current plan, we just handle the control's `TouchEnter`/`TouchLeave` .etc event, and popup/hide the virtual keyboard, we do not know whether WPF can offer simpler way to do this. –  Oct 06 '13 at 06:27
  • So is the issue that you want the virtual keyboard to dismiss when you're *not* touching a text control? Because I thought [that happens automatically if you set the right automation properties](http://msdn.microsoft.com/en-us/library/windows/apps/hh465404.aspx). – Eric Brown Oct 06 '13 at 06:29
  • @EricBrown Hi, thanks for your link. From that page, it seems that we do not need to do any additional work if we use the native control instead of custom control, but we've written a very simple demo which includes some buttons and a TextBox, when the TextBox got focused, the virtual keyboard didn't show up. Or still need to set some property on the native control? –  Oct 06 '13 at 06:43
  • Use [UIVerify](http://msdn.microsoft.com/en-us/library/windows/desktop/hh920986(v=vs.85).aspx) to verify that your automation tree is correct. – Eric Brown Oct 07 '13 at 02:52

8 Answers8

31

Try this,

First check for a physical keyboard presence:

KeyboardCapabilities keyboardCapabilities = new Windows.Devices.Input.KeyboardCapabilities();
return  keyboardCapabilities.KeyboardPresent != 0 ? true : false;

If you do not find a physical keyboard, use the in-built virtual keyboard of windows:

Process.Start(Environment.GetFolderPath(Environment.SpecialFolder.System) + Path.DirectorySeparatorChar + "osk.exe");

Got help from here: link 1 link 2

Community
  • 1
  • 1
Savaratkar
  • 1,974
  • 1
  • 24
  • 44
  • 1
    So WPF cannot easily achieve this by setting some property or very few other settings naturally? Actually we also post another question related `osk.exe` stuff(http://stackoverflow.com/questions/19088686/how-to-make-tabtip-exe-window-maximum-when-popup-it), in general we don't think it's a good solution since we have to control a lot of stuff to meet user friendly design. –  Oct 08 '13 at 08:34
  • This is really the only way to get the behaivor you want. If you want the current Input element to be in visual state, you could resize the CurrentWindow in order to not overlay the on-screen keyboard. (In fact creating a custom textbox, with the described feature is really easy and works smothlessly. Be sure to check if the OS is W8 before you open the keyboard. – Florian Oct 09 '13 at 12:09
  • 10
    This namespace is only present in the WinRT framework. Is there an equivalent for plain old .Net WPF? – Mathias Becher Jan 21 '15 at 11:38
  • How would you set the position of the keyboard? – lahjaton_j Apr 19 '16 at 08:55
  • Interesting, What is the Path varaible? – digitai Jul 19 '17 at 19:06
  • 1
    i think "TabTip.exe" is usually better than "osk.exe" – 00jt Dec 31 '18 at 04:39
7

I've published a sample on how to trigger the touch keyboard in WPF applications when a user clicks into a Textbox, its here:

http://code.msdn.microsoft.com/Enabling-Windows-8-Touch-7fb4e6de

It has been something I've been working on for many months, i'm glad to finally contribute this example to our community. Please let me know if there are any questions, suggestions, problems, etc in the sample Q&A pane

Dmitry Lyalin
  • 444
  • 5
  • 8
  • 3
    Works pretty well but it disables scrolling controls like ScrollViewer. I ended up launching tabtip.exe on GotFocus using EventManager.RegisterClassHandler(). – LMeyer Jan 24 '14 at 10:34
5

I created a library to automate everything concerning TabTip integration in WPF app.

You can get it on nuget, and after that all you need is a simple config in your apps startup logic:

TabTipAutomation.BindTo<TextBox>();

You can bind TabTip automation logic to any UIElement. Virtual Keyboard will open when any element of specified type will get focus, and it will close when element will lose focus. Not only that, but TabTipAutomation will move UIElement (or Window) into view, so that TabTip will not block focused element.

For more info refer to the project site.

Max
  • 310
  • 3
  • 7
3

This solution is very simple: http://code.msdn.microsoft.com/windowsapps/Enabling-Windows-8-Touch-7fb4e6de

The steps are detailed in the link above, here is the short version:

  • Add a UIAutomationClient reference
  • Use IFrameworkInputPane from managed code (DLL at link or convert inputpanelconfiguration.idl to DLL, see steps below)
  • Create new class InkInputHelper to disable inking support (code below)
  • Call InkInputHelper.DisableWPFTabletSupport(); from MainWindow constructor or similar
  • Add using System.Windows.Interop;
  • Add to MainWindow_Loaded or similar:

        System.Windows.Automation.AutomationElement asForm =
        System.Windows.Automation.AutomationElement.FromHandle(new WindowInteropHelper(this).Handle);
        InputPanelConfigurationLib.InputPanelConfiguration inputPanelConfig = new InputPanelConfigurationLib.InputPanelConfiguration();
        inputPanelConfig.EnableFocusTracking();
    

To convert inputpanelconfiguration.idl to DLL

On Windows 8.1: c:\Program Files (x86)\Windows Kits\8.1\Include\um\inputpanelconfiguration.idl

To build a DLL from the IDL use the following steps:

  • Start a command prompt
  • Use the MIDL compiler tool to build a Type Library TLB file
    • Example: midl /tbld {filename}
  • Use the TLBIMP tool to convert the above generated Type Library (TLB file) into a DLL that .NET can use by running the following command
    • Example: TLBIMP.exe InputpanelConfiguration.tlb /publickey:{pathToKey} /delaysign

InkInputHelper class:

using System;
using System.Reflection;
using System.Windows.Input;

namespace ModernWPF.Win8TouchKeyboard.Desktop
{
public static class InkInputHelper
{
    public static void DisableWPFTabletSupport()
    {
        // Get a collection of the tablet devices for this window.  
        TabletDeviceCollection devices = System.Windows.Input.Tablet.TabletDevices;

        if (devices.Count > 0)
        {
            // Get the Type of InputManager.
            Type inputManagerType = typeof(System.Windows.Input.InputManager);

            // Call the StylusLogic method on the InputManager.Current instance.
            object stylusLogic = inputManagerType.InvokeMember("StylusLogic",
                        BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                        null, InputManager.Current, null);

            if (stylusLogic != null)
            {
                //  Get the type of the stylusLogic returned from the call to StylusLogic.
                Type stylusLogicType = stylusLogic.GetType();

                // Loop until there are no more devices to remove.
                while (devices.Count > 0)
                {
                    // Remove the first tablet device in the devices collection.
                    stylusLogicType.InvokeMember("OnTabletRemoved",
                            BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
                            null, stylusLogic, new object[] { (uint)0 });
                }
            }
        }
    }
}
}

This should work. Again, much better info and a downloadable sample in the link. I only copy pasted the basics for archiving purposes.

gakera
  • 3,589
  • 4
  • 30
  • 36
2

When targeting .Net 4.6.2+ you don't need to do anything:

https://riptutorial.com/wpf/example/23104/showing-touch-keyboard-on-windows-8-and-windows-10

itsho
  • 4,640
  • 3
  • 46
  • 68
  • 1
    @00jt interesting. Do you have the [latest Windows 10 version](https://learn.microsoft.com/en-us/windows/windows-10/release-information)? (1809) – itsho Dec 31 '18 at 16:16
  • The tablet i'm using is running Windows 10 Pro (10.0.17134 Build 17134) – 00jt Dec 31 '18 at 21:25
  • 1
    @00jt 10.0.17134 is Version 1803 (April 2018 Update). I guess you have Tablet mode enabled. if it still doesn't work - please try the `Windows 10 Settings approach` section on the original link. – itsho Jan 01 '19 at 09:34
  • table mode doesn't seem to affect it (For the record, i'm using PasswordBoxes), Also i already have "show the keyboard or handwriting panel when not in table mode" checked as well – 00jt Jan 02 '19 at 06:49
  • 1
    @00jt I've tested on Windows 10 1709 and compiled with 4.6.2. it works ONLY if you use the touch screen, and NOT if you use the mouse to click the field. also tested on PasswordBox - see [gist](https://gist.github.com/itsho/58d9cc7ad66d898216454cecb183ea1d) I've just created – itsho Jan 02 '19 at 10:48
  • I am in tablet mode, and i am touching the screen with my finger. TextBox works fine for me, PasswordBox does not. This whole time I had just been using PasswordBox... In fact, when i have a TextBox and PasswordBox and editable ComboBox on the same window, touching the TextBox opens the TabTip, touching the ComboBox keeps it open, but then then touching the PasswordBox closes it. Touching the ComboBox or TextBox again opens it up again. Since i have "show the keyboard or handwriting panel when not in table mode", it also behaves the same in Desktop Mode. (PasswordBox still fails) – 00jt Jan 02 '19 at 15:51
  • 1
    @00jt that's really weird :-( I've tested again on **few** other PC and tablets, and it behaves correctly for both `TextBox` **and** `PasswordBox`. Out of curiosity: did you try the example gist I provided above? – itsho Jan 03 '19 at 12:56
  • yes, using your code (compiled to 4.7.2) run on Windows 10 v1803. The Textboxes work just fine, but the Passwordboxes behave strangely. If i repeatedly touch a textbox, the tab-tip stays open. But it closes when i touch a Password box. I did discover if i long-press the password box, then touch 'copy' - then the TabTip opens. And every time i touch the PasswordBox after that, the TabTip alternates between opening & closing. ..... so strange. – 00jt Jan 04 '19 at 16:36
  • And now (after pasting text into the password box a second time), it seems to behave normally.... what is going on?? Every time i start over the application, at first the PasswordBox doesn't work right away.. then after long-clicking/pasting it finally starts working correctly... – 00jt Jan 04 '19 at 16:38
  • 2
    @00jt I really don't know what to say, but maybe you can utilize one of [Microsoft's examples](https://github.com/Microsoft/WPF-Samples/blob/fbee9021ef17407bc3aaa79ff282f25178a0c4af/Input%20and%20Commands/TouchKeyboard/TouchKeyboardNotifier/Readme.md) in GitHub – itsho Jan 05 '19 at 16:28
1

I saw this done in a TechEd session, you need to first disable Inking support (DisableWPFTabletSupport) then you can create an InputPanelConfiguration (AutomationElement.FromHandle(new WindowsInteropHelper(this).Handle) and call EnableFocusTracking.

DisableWPFTabletSupport: http://msdn.microsoft.com/en-us/library/ee230087.aspx

EnableFocusTracking: http://msdn.microsoft.com/en-us/library/windows/desktop/jj126268(v=vs.85).aspx

user895440
  • 31
  • 1
  • 3
  • Do you have a link to the TechEd session? Unfortunately for me EnableFocusTracking is only supported on Win8 or higher.. – Wouter Oct 31 '13 at 08:40
0
 public static class InkInputHelper
    {
        public static void DisableWPFTabletSupport()
        {
            // Get a collection of the tablet devices for this window.  
            TabletDeviceCollection devices = System.Windows.Input.Tablet.TabletDevices;

            if (devices.Count > 0)
            {
                // Get the Type of InputManager.
                Type inputManagerType = typeof(System.Windows.Input.InputManager);

                // Call the StylusLogic method on the InputManager.Current instance.
                object stylusLogic = inputManagerType.InvokeMember("StylusLogic",
                            BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                            null, InputManager.Current, null);

                if (stylusLogic != null)
                {
                    //  Get the type of the stylusLogic returned from the call to StylusLogic.
                    Type stylusLogicType = stylusLogic.GetType();

                    // Loop until there are no more devices to remove.
                    while (devices.Count > 0)
                    {
                        // Remove the first tablet device in the devices collection.
                        stylusLogicType.InvokeMember("OnTabletRemoved",
                                BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
                                null, stylusLogic, new object[] { (uint)0 });
                    }
                }
            }
        }
    }

use that class to determine if there is a physical keyboard, or a similar way that might better suit your needs.

i used this class to open and close the keyboard wherever i wanted.

 class KeyboardManager
    {

        public static void LaunchOnScreenKeyboard()
        {
            var processes = Process.GetProcessesByName("osk").ToArray();
            if (processes.Any())
                return;
            string keyboardManagerPath = "KeyboardExecuter.exe";
           Process.Start(keyboardManagerPath);
        }

        public static void KillOnScreenKeyboard()
        {
            var processes = Process.GetProcessesByName("osk").ToArray();
            foreach (var proc in processes)
            {
                proc.Kill();
            }
        }
        public static void killTabTip()
        {
            var processes = Process.GetProcessesByName("TabTip").ToArray();
            foreach (var proc in processes)
            {
                proc.Kill();
            }
        }

        public static void LaunchTabTip()
        {
            Process.Start("TabTip.exe");
        }
    }

keep in mind the following: i added a copy of both osk.exe AND tabtip.exe. adding this in my program solved an issue where either tabtip or osk wouldnt work on 32/64 bits.

osk is the keyboard, tabtip is the docked version of it. keyboardexecuter is a program i made which is used as a fallback method.

note* i can currently not test this on a touch screen device. you have to try that on your own.

for this to all work correctly i used this code in my mainwindow:

public int selectedTableNum;
        public MainWindow()
        {
            InitializeComponent();

            Loaded += MainWindow_Loaded;

            // Disables inking in the WPF application and enables us to track touch events to properly trigger the touch keyboard
            InkInputHelper.DisableWPFTabletSupport();
            //remove navigationbar
            Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
            {
                var navWindow = Window.GetWindow(this) as NavigationWindow;
                if (navWindow != null) navWindow.ShowsNavigationUI = false;
            }));


            KeyboardManager.LaunchTabTip();

        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            //Windows 8 API to enable touch keyboard to monitor for focus tracking in this WPF application
            InputPanelConfiguration cp = new InputPanelConfiguration();
            IInputPanelConfiguration icp = cp as IInputPanelConfiguration;
            if (icp != null)
                icp.EnableFocusTracking();
            mainFrame.Content = new LoginPage();
        }
        //public bool HasTouchInput()
        //{
        //    foreach (TabletDevice tabletDevice in Tablet.TabletDevices)
        //    {
        //        //Only detect if it is a touch Screen not how many touches (i.e. Single touch or Multi-touch)
        //        if (tabletDevice.Type == TabletDeviceType.Touch)
        //            return true;
        //    }

        //    return false;
        //}

i included the comments because it might be usefull to someone if there is an error.

the inputpanel configuration:

[Guid("41C81592-514C-48BD-A22E-E6AF638521A6")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInputPanelConfiguration
{
    /// <summary>
    /// Enables a client process to opt-in to the focus tracking mechanism for Windows Store apps that controls the invoking and dismissing semantics of the touch keyboard.
    /// </summary>
    /// <returns>If this method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.</returns>
    int EnableFocusTracking();
}

[ComImport, Guid("2853ADD3-F096-4C63-A78F-7FA3EA837FB7")]
class InputPanelConfiguration
{
}

i hope this might help future visitors of this question.

-2

The easiest option when not requiring SecureString output is to use TextBox and use something like Wingdings as font.

Jiri Kral
  • 1
  • 2