Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Lenovo Horizon Striker

4.27/5 (3 votes)
20 Nov 2013CPOL2 min read 32K   123  
Detecting a Lenovo Horizon Striker accessory with a WPF application

Image 1

Introduction

In this article I will provide a simple example of how to get a WPF application to detect a Lenovo striker. The Lenovo striker is an accessory for the Lenovo Horizon All-in-One PC. The striker can be used with some of the pre-loaded Horizon games, like Lonovo Air Hockey, by sliding it around the touchscreen surface. Since, as of the time of writing, there is no documentation on how to use the striker accessory with a WPF application this article will hopefully be a useful and inspirational guide. The following video shows the sample application in action.

Image 2

Striker Accessory

The striker essentially acts as a stylus. At the bottom of the striker there are four circular pads, two of which provide stylus input.

Image 3

The two larger pads act as contact points for stylus input. To make use of the striker in my WPF sample I will find the points of contact, on the screen, of each of the two contact pads; find the 'center' of the striker using the two sets of coordinates; then place an Ellipse directly under the striker.

The XAML markup for the sample project is a simple affair,

XML
<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="LenovoStriker" Height="514" Width="678" WindowState="Maximized">
    <Grid>
        <Canvas x:Name="StylusCanvas" Background="White" StylusDown="StylusCanvas_StylusDown"
                StylusMove="StylusCanvas_StylusMove" StylusUp="StylusCanvas_StylusUp"/>        
    </Grid>
</Window>

Notice that I will be handling some stylus events; specifically the StylusDown,

StylusMove
, and StylusUp events. An Ellipse will be added to the Canvas when the striker is placed on the screen and will follow the striker as it is moved around the screen.

VB.NET
Class MainWindow
    Private strikerPads As New Dictionary(Of Integer, Point)()
    Private diameter As Integer = 160
    Private radius As Integer = 80
    Private strikerEllipse As New Ellipse With {.Height = diameter, .Width = diameter,
                                                .Stroke = Brushes.Black, .StrokeThickness = 2,
                                                .Fill = Brushes.Yellow}

    Private Sub StylusCanvas_StylusDown(sender As Object, e As StylusDownEventArgs)
        Dim padPoint As Point = e.GetPosition(StylusCanvas)
        strikerPads.Add(e.StylusDevice.Id, padPoint)

        If (strikerPads.Count = 2) Then
            PositionEllipse()
            StylusCanvas.Children.Add(strikerEllipse)
        End If
    End Sub

    Private Sub StylusCanvas_StylusMove(sender As Object, e As StylusEventArgs)
        Dim padPoint As Point = e.GetPosition(StylusCanvas)
        strikerPads(e.StylusDevice.Id) = padPoint
        PositionEllipse()
    End Sub

    Private Sub PositionEllipse()
        Dim pad_1 As Point = strikerPads.First.Value
        Dim pad_2 As Point = strikerPads.Last.Value

        Dim y1 As Integer = CInt(pad_1.Y)
        Dim y2 As Integer = CInt(pad_2.Y)
        Dim x1 As Integer = CInt(pad_1.X)
        Dim x2 As Integer = CInt(pad_2.X)

        Dim yDiff As Integer = Math.Abs(y1 - y2)
        Dim xDiff As Integer = Math.Abs(x1 - x2)

        Dim x As Integer
        Dim y As Integer

        If (x1 < x2) Then
            x = x1 + (xDiff / 2) - radius
        Else
            x = x2 + (xDiff / 2) - radius
        End If

        If (y1 < y2) Then
            y = y1 + (yDiff / 2) - radius
        Else
            y = y2 + (yDiff / 2) - radius
        End If

        Canvas.SetLeft(strikerEllipse, x)
        Canvas.SetTop(strikerEllipse, y)
    End Sub
  
    Private Sub StylusCanvas_StylusUp(sender As Object, e As StylusEventArgs)
        If (StylusCanvas.Children.Contains(strikerEllipse)) Then
            StylusCanvas.Children.Remove(strikerEllipse)
        End If
        strikerPads.Remove(e.StylusDevice.Id)
    End Sub
End Class
C#
namespace LenovoStriker
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private Dictionary<int, Point> strikerPads = new Dictionary<int,Point>();
        private static int diameter = 160;
        private static int radius = 80;
        private Ellipse strikerEllipse = new Ellipse()
        {
            Height = diameter,
            Width = diameter,
            Stroke = Brushes.Black,
            StrokeThickness = 2,
            Fill = Brushes.Yellow
        };

        private void StylusCanvas_StylusDown(object sender, StylusDownEventArgs e)
        {
            Point padPoint = e.GetPosition(StylusCanvas);
            strikerPads.Add(e.StylusDevice.Id, padPoint);

            if (strikerPads.Count == 2)
            {
                PositionEllipse();
                StylusCanvas.Children.Add(strikerEllipse);
            }
        }

        private void StylusCanvas_StylusMove(object sender, StylusEventArgs e)
        {
            Point padPoint = e.GetPosition(StylusCanvas);
            strikerPads[e.StylusDevice.Id] = padPoint;            
            PositionEllipse();               
        }

        private void PositionEllipse()
        {
            Point pad_1 = strikerPads.First().Value;
            Point pad_2 = strikerPads.Last().Value;

            int y1 = (int)pad_1.Y;
            int y2 = (int)pad_2.Y;
            int x1 = (int)pad_1.X;
            int x2 = (int)pad_2.X;

            int yDiff = Math.Abs(y1 - y2);
            int xDiff = Math.Abs(x1 - x2);

            int x;
            int y;

            if (x1 < x2)
            {
                x = x1 + (xDiff / 2) - radius;
            }
            else
            {
                x = x2 + (xDiff / 2) - radius;
            }

            if (y1 < y2)
            {
                y = y1 + (yDiff / 2) - radius;
            }
            else
            {
                y = y2 + (yDiff / 2) - radius;
            }

            Canvas.SetLeft(strikerEllipse, x);
            Canvas.SetTop(strikerEllipse, y);
        }

        private void StylusCanvas_StylusUp(object sender, StylusEventArgs e)
        {
            if (StylusCanvas.Children.Contains(strikerEllipse))
            {
                StylusCanvas.Children.Remove(strikerEllipse);
            }
            strikerPads.Remove(e.StylusDevice.Id);
        }
    }
}

Each of the contact pads will have a stylus device ID which is used as a key in a Dictionary collection whose Values are the on-screen coordinates of the pads. Now if I was to place the Ellipse on the Canvas, at the calculated coordinates of the striker center, the Ellipse would be greatly offset.

Image 4

Remember that in WPF the coordinate points of an Ellipse are at the top-left corner. For proper alignment I subtract the radius of the Ellipse from the calculated values in the PositionEllipse() method.

Conclusion

I hope that you found the information in this article useful, especially those who received a AIO in the Intel AIC 2013 competition. Maybe some of the apps that were submitted in Round Two could make use of this interesting accessory.

History

  • 20th Nov 2013: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)