Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Touch Screen Friendly WPF Edit Boxes with Own Keypads

0.00/5 (No votes)
9 Jun 2011 1  
In this article I would like to present these controls here for the benefit of someone that decides to use touch screen only user interface in their next design.

Introduction

For a machine we were building last year we decided to use a touch screen only user interface. This machine, with its 40 axes of motion and 500 over I/O points with all of them acting parallel, needed a very responsive UI that updated status information such as moving motor positions, changing digital I/O status of processes etc. without much load on the software. So, we came up with a comprehensive industrial UI for this machine, using WPF and C#, that has LED controls that reflected associated digital I/O status, text controls that displayed changing numbers such as motor positions or analog I/O status, buttons that were capable of showing status of the operations that they initiated etc. As with the decision to use only touch screen and not keyboard or mouse for user interface came the requirement of an on screen keyboard. Initially, we thought we would manage with the OS supplied keyboard that comes with Windows. However, we were proven wrong by the unusability of it to the task simply dues to the lack of control that we could exercise over it using our application as well as the size of the keys that we quite unfriendly for industrial users that often wear gloves. So, we came up with two controls that derive themselves from the WPF TextBox control. These derived controls, when touched, would display a popup keypad that depending on the type of control, would be numeric keypad or alphanumeric keypad. I would like to present these controls here for the benefit of someone that decides to use touch screen only user interface in their next design.

numbox.jpg

alphanumbox.jpg

Background

WPF, by design, allows us to transform the look and feel and to certain extent the behavior of all the UI controls using Styles and Templating features. Most often we do not need to use inheritance to derive a new control as we did with old MS technologies such as MFC etc. However, WPF designers have left room for us to derive new UI controls if we really needed to. The two controls that I am going to be talking about here are derived from the traditional TextBox control. The only difference between these two controls are in the way they construct their keypads. There are other ways, such as deriving a single control from TextBox with a virtual method for constructing keypad and have derived these two controls from that control and overidded the virtual method. For clarity and for simplicity, I have derived two different control that are very much alike, except in the way they construct their keypads. That would be another discussion, which I am not inclined to get in to here. The derived control overrides the OnMouseDown event, that is triggered when the control is touched. In OnMouseDown event handler code, a Popup is created in code and is displayed if one is not already created for this control. Same event is also used to close a displayed Popup. Functionally, if one of these controls are touched a keypad is dropped down and once the key entries are made, the control is touched again to hide the keypad.

Using the Code

For this article, the code for TouchNumericBox control class and TouchAlphaNumericBox control class are provided, under a name space SiriusMicrotech.core.UI, in a single file named AlphaNumericEditBox.cs. The styling information that we used for our machine's UI theme is provided in TouchStyle.xaml. TouchStyle.xaml also provides styling information for the popup keypad keys. These can be modified, as they are intended, to suit individual preferences. We used 40 pixels as the key size for the keypad keys and chose the size of the text box in such a way that the keypad, when popped, will be bigger than the text box by 10 pixels on both sides and nicely centered. To use these controls in your project, add these two files, AlphaNumericEditBox.cs and TouchStyle.xaml, in to your project. To use these controls in a window, you have to first refer to the name space in your window's XAML code by placing the name space reference in the Window properties. To access the styling information in TouchStyle.xaml, you need to place the following Window.Resources section in your WPF window XAML representation. Window.Resources section is usually placed just after the Window section of the XAML code, just above the main container panel section. In our demo, we have Grid as the main container panel for our Window.

Following code shows how the name space reference and Window.Resources section looks in our demo code that is attached with this article.

<Window x:Class="AlphanumericEditbox.TouchScreen"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:SiriusMicrotech="clr-namespace:SiriusMicrotech.core.UI"
    Title="Touch Screen Friendly Edit Box Controls Demonstration" 
        SizeToContent="WidthAndHeight" >
	<Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="TouchBox\TouchStyle.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
...

Here is the code for OnMouseDown event handler for the TouchNumericBox class. This is where we programmatically construct the keypad.

        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            if (popKpd != null)     // Check if we already have a keypad created
            {
                popKpd.IsOpen ^= true;   // if we have a keypad, toggle its open status
            }
            else
                // Contruct a keypad as a popup
            {
                popKpd = new Popup();
                popKpd.Placement = PlacementMode.Bottom;
                popKpd.PopupAnimation = PopupAnimation.Scroll;
                kpd = new Grid();

                for (int i = 0; i < 4; i++)
                {
                    kpd.ColumnDefinitions.Add(new ColumnDefinition());
                    kpd.RowDefinitions.Add(new RowDefinition());
                }
                int btn = 1;
                for (int row = 2; row >= 0; row--)
                {
                    for (int col = 0; col < 3; col++)
                        populateGridPos(row, col, Convert.ToString(btn++));
                }
                populateGridPos(3, 0, "0");
                populateGridPos(3, 1, ".");
                populateGridPos(3, 2, "CLR");

                popKpd.Child = kpd;
                popKpd.IsOpen = false;

                popKpd.PlacementTarget = this;
                popKpd.Placement = PlacementMode.Bottom;
                popKpd.HorizontalOffset = -10;
                popKpd.IsOpen = true;
            }
            base.OnMouseDown(e);
        }

The helper function populateGridPos is used to create a button in the given cell of the keypad grid. This function creates a button with a fixed size of 40 pixels and assigns an event handler for the click event. The button click handler is where we update the text in the text box. The code for populateGridPos and button_click are as follows.

        private void populateGridPos(int row, int col, string btn)
        {
            var button = new Button();
            button.Style = (Style)FindResource("TouchKey");
            button.Content = btn;
            button.Width = 40;
            button.Click += new RoutedEventHandler(button_Click);
            Grid.SetRow(button, row);
            Grid.SetColumn(button, col);
            this.kpd.Children.Add(button);
        }

        void button_Click(object sender, RoutedEventArgs e)
        {
            if (this.IsReadOnly) return;
            Button btn = (Button)e.OriginalSource;
            string s = btn.Content.ToString();
            if (s == "CLR")
            {
                this.Text = "";
            }
            else
            {
                this.Text += s;
            }
        }

Points of Interest

Even though we derive these controls from native TextBox control in WPF, we need to specify the ScrollViewer property, which is the key to display and use the functionality of a text box. Mere inheritance did not bring all the TextBox control functionality here. This took as a little bit of time to figure out. The other weird thing that took as a while to work around was a problem with DefaultStyleKeyProperty.OverrideMetadata call that was throwing an exception when we tried to use more than one instance of these controls. For now, we have overcome this by surrounding this call with a try-catch block.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here