30
votes

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.

8
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
How are users without a physical keyboard going to enter text into your application?Eric Brown
@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.user1031200
@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.user1031200
I'm still confused, then. There are more tablets than Surface Pro. Is the distinguishing characteristic the presence of a physical keyboard?Eric Brown

8 Answers

31
votes

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

6
votes

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

4
votes

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.

3
votes

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.

1
votes

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

0
votes
 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
votes

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