Home / C# / WPF touch-friendly controls for resistive touch-screen

WPF touch-friendly controls for resistive touch-screen

While it’s fairly common, when working in automation field, to make software that runs on resistive touch-screens, I admit that it’s not a trivial task to make a good user experience, especially with a desktop technology like WPF and C#.
In this article I posted some of the problems that I met when creating touch UI and how I solved the most of them.

From physical switches to WPF controls

On electrical cabinets there are usually this kind of components: push buttons, on-off switches, switches with multiple positions (es.: automatic mode- semi automatic mode – manual mode), pilot lamps, etc…
electrical cabinet
To convert those devices to “software controls” there are 2 options, one is to design (buy) them, like the AdvancedHmi, and another one is by using standard controls.
This is my table of conversion when using standard controls:
Push buttons -> Window buttons
On-Off switch -> Checkbox
Multi-state switch -> Radio button
Pilot lamp -> led or other kind of visual colored controls.

Choosing the size of the controls

getactouch
The most common scenario for the user of your software is a person that is wearing work gloves and that doesn’t want that the touch screen interaction slows down his daily work.
There are 2 aspects to consider, one is that the controls should be large enough, and the second one is that the software must be good-looking, because to make a “supermarket touchscreen” there are easier product out there rather than Visual Studio and WPF.
CUSTOM_KT 10
About the size of the controls, it’s clear that there is a limit on how big we can make the control, so the trick is to them as bigger as possible, then create a large touch area and put all controls one near the other, without margins, so every time the operator clicks, he will not miss the target.

CheckBoxes and RadioButtons

To show you an example of friendly touch controls i choosed as base the simple styles from Microsoft. If you are interested in the package you can download it from my github fork, but in this post I will use only the CheckBox and RadioButton styles.
This is the original checkbox style:


<ControlTemplate TargetType="CheckBox">
    <BulletDecorator Background="Transparent">
        <BulletDecorator.Bullet>
             <Border x:Name="Border" 
                     Width="13" 
                     Height="13" 
                     CornerRadius="0" 
                     Background="{StaticResource NormalBrush}"
                     BorderThickness="1"
                     BorderBrush="{StaticResource NormalBorderBrush}">
                <Path Width="7" Height="7" 
                      x:Name="CheckMark"
                      SnapsToDevicePixels="False" 
                      Stroke="{StaticResource GlyphBrush}"
                      StrokeThickness="2"
                      Data="M 0 0 L 7 7 M 0 7 L 7 0" />
           </Border>
       </BulletDecorator.Bullet>
       <ContentPresenter Margin="4,0,0,0"
                         VerticalAlignment="Center"
                         HorizontalAlignment="Left"
                         RecognizesAccessKey="True"/>
     </BulletDecorator>

The first thing to do is to enlarge the square button, and to do this we need to enlarge both the “Border” part, from 13x13px to 22x22px, then enlarge the “Path” part to make the X fit the checkbox.
This is the new code:

<!-- Checkbox square, enlarged from 13px to 22px -->
<Border BorderThickness="1" BorderBrush="Black" Padding="1" VerticalAlignment="Center" Margin="10,16">
    <BulletDecorator Background="Transparent">
        <BulletDecorator.Bullet>
            <Border x:Name="Border"  
                    Width="22" 
                    Height="22" 
                    CornerRadius="0" 
                    Background="{StaticResource NormalBrush}"
                    BorderThickness="1"
                    BorderBrush="{StaticResource NormalBorderBrush}">
                <Path 
                      Width="13" Height="13" 
                      x:Name="CheckMark"
                      SnapsToDevicePixels="False" 
                      Stroke="{StaticResource GlyphBrush}"
                      StrokeThickness="2"
                      Data="M 0 0 L 13 13 M 0 13 L 13 0" />
            </Border>
        </BulletDecorator.Bullet>
        <ContentPresenter Margin="4,0,0,0"
                          VerticalAlignment="Center"
                          HorizontalAlignment="Left"
                          RecognizesAccessKey="True"/>
    </BulletDecorator>
</Border>

Standard controls
As you can see the checkboxes are quite large, but they are not large enough for a user with gloves, because his fingers are much bigger than the control, and this will cause the operator to often miss the target.
To avoid this issue we are going to create a large invisible touch area outside the control, and to do this we have to surround the control with a grid, and add inside the grid a rectangle filled with Transparent color. It is really important to fill it with a color (transparent in our scenario), because if you leave the Fill property blank (or null) the rectangle will not be rendered and will not activate the control.
This is the code:

<ControlTemplate TargetType="CheckBox">
    <Grid>                            
        <!-- This is the touch area, the trick is to create a rectangle Filled with transparent. -->
        <!-- If you don't fill the rectangle, you will not be able to click it. -->
        <!-- The border is just to show you the touch area, it's not needed for the template -->
        <Border BorderThickness="1" BorderBrush="Black" Margin="0,1">                                
            <Rectangle x:Name="touchArea" Fill="Transparent"  Height="50"/>
        </Border>
        <!-- Checkbox square, enlarged from 13px to 22px -->
        <BulletDecorator Background="Transparent" VerticalAlignment="Center" Margin="10,0,0,0">
            <BulletDecorator.Bullet>
                <Border x:Name="Border"  
                        Width="22" 
                        Height="22" 
                        CornerRadius="0" 
                        Background="{StaticResource NormalBrush}"
                        BorderThickness="1"
                        BorderBrush="{StaticResource NormalBorderBrush}">
                    <Path 
                          Width="13" Height="13" 
                          x:Name="CheckMark"
                          SnapsToDevicePixels="False" 
                          Stroke="{StaticResource GlyphBrush}"
                          StrokeThickness="2"
                          Data="M 0 0 L 13 13 M 0 13 L 13 0" />
                </Border>
            </BulletDecorator.Bullet>
            <ContentPresenter Margin="4,0,0,0"
                              VerticalAlignment="Center"
                              HorizontalAlignment="Left"
                              RecognizesAccessKey="True"/>
        </BulletDecorator>
    </Grid>

In the screenshot below, you can see that the touch area of the checkbox is large enough even if the control is not that big, but most important, the operator will never miss the click.
TouchFriendly

Buttons

Push buttons are different from Windows buttons, because the click action on physical buttons happens when you push it, instead on Windows buttons the click happens when you release the button.
The first approach to emulate a physical button is with PreviewMouseLeftButtonDown and PreviewMouseLeftButtonUp. This approach unfortunately leads to lot of complexity, because you have to write down all the cases when the button is pressed and lose focus for various reasons (the most common is a messagebox, that may not necessary come from your application).
What i usually do to avoid this kind of problems is to subclass the button and create an event PressedChanged, and i use a “IsPressed” bool argument to make the actions.
The code is really simple, just create a UserControl, rename it as button and define the event and eventhandler, like in the code below:

<Button x:Class="SimpleStyles.PushButton"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        mc:Ignorable="d" 
        d:DesignHeight="300" d:DesignWidth="300"/>
public delegate void PressedChangedEventHandler(object sender, bool IsPressed);
    
public partial class PushButton : Button
{
    public event PressedChangedEventHandler PressedChanged;

    public PushButton()
    {
        InitializeComponent();
    }

    protected override void OnIsPressedChanged(DependencyPropertyChangedEventArgs e)
    {
        OnPressedChanged();
        base.OnIsPressedChanged(e);
    }

    private void OnPressedChanged()
    {
        if (PressedChanged != null)
            PressedChanged(this, this.IsPressed);
    }
}

This example shows you the difference between the two buttons when they lose focus. If you keep a button pressed for more than 3 seconds a messagebox popup, and it’s easy to verify that the checkbox of the PushButton gets cleared, while the checkbox of the normal button is not cleared. PressedChangedButton

Sample application

As usual you can download the sample application on my github repository.

Bonus: GUI frameworks

To achieve a good looking design, there are some open-source framework that can be used and which have some good minimalistic graphics, like MahApps framework or Modern UI for WPF.
Minimalistic UI is a good approach to obtain a modern look for your application without wasting too much energies on the design, because there is plenty of examples and icons in internet and most of them are free.
You can find an example of a scientific application at this link.
of-scripting

One comment

  1. +1 for MahApps.Metro and ModernUI references. Nice introduction and I look forward to seeing more on GitHub.

Leave a Reply to mu_killnine Cancel reply

Your email address will not be published.