Click here to Skip to main content
15,125,228 members
Articles / Desktop Programming / WPF
Article
Posted 19 Nov 2013

Tagged as

Stats

25.8K views
118 downloads
3 bookmarked

Lenovo Horizon Striker

Rate me:
Please Sign up or sign in to vote.
4.27/5 (3 votes)
20 Nov 2013CPOL2 min read
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)

Share

About the Author

Meshack Musundi
Software Developer
Kenya Kenya
Experienced C# software developer with a passion for WPF.

Awards,
  • CodeProject MVP 2013
  • CodeProject MVP 2012
  • CodeProject MVP 2021

Comments and Discussions

 
QuestionLenovo Horizon EDice Pin
ThPratik20-Nov-13 7:52
MemberThPratik20-Nov-13 7:52 
AnswerRe: Lenovo Horizon EDice Pin
Meshack Musundi20-Nov-13 8:00
mvaMeshack Musundi20-Nov-13 8:00 
GeneralRe: Lenovo Horizon EDice Pin
linkedPIXEL20-Nov-13 8:58
MemberlinkedPIXEL20-Nov-13 8:58 
GeneralRe: Lenovo Horizon EDice Pin
Meshack Musundi20-Nov-13 9:34
mvaMeshack Musundi20-Nov-13 9:34 
GeneralRe: Lenovo Horizon EDice Pin
linkedPIXEL20-Nov-13 12:31
MemberlinkedPIXEL20-Nov-13 12:31 
GeneralRe: Lenovo Horizon EDice Pin
ThPratik20-Nov-13 18:37
MemberThPratik20-Nov-13 18:37 
GeneralRe: Lenovo Horizon EDice Pin
linkedPIXEL20-Nov-13 18:48
MemberlinkedPIXEL20-Nov-13 18:48 
GeneralRe: Lenovo Horizon EDice Pin
Adam David Hill21-Nov-13 9:39
professionalAdam David Hill21-Nov-13 9:39 
QuestionTouch Count Issue Pin
Adam David Hill20-Nov-13 0:19
professionalAdam David Hill20-Nov-13 0:19 
The Striker won't always produce exactly two touches. It produces from two to three, as the smaller pads also register when the striker is leaned to one side. I'm not sure why they're built that way and what it's meant to be used for, but it should be considered when developing with them, as lines such as this are making some assumptions which could cause problems:

C#
if (strikerPads.Count == 2)


I love that you've included both the VB & C#, though.
Check out my latest article: Hot Shots, my App Innovation contest entry for 2013.

AnswerRe: Touch Count Issue Pin
Meshack Musundi20-Nov-13 0:51
mvaMeshack Musundi20-Nov-13 0:51 
GeneralRe: Touch Count Issue Pin
Adam David Hill20-Nov-13 1:07
professionalAdam David Hill20-Nov-13 1:07 
GeneralRe: Touch Count Issue Pin
Meshack Musundi20-Nov-13 1:13
mvaMeshack Musundi20-Nov-13 1:13 
QuestionYes, but ... Pin
Florian Rappl19-Nov-13 23:00
professionalFlorian Rappl19-Nov-13 23:00 
AnswerRe: Yes, but ... Pin
Meshack Musundi19-Nov-13 23:39
mvaMeshack Musundi19-Nov-13 23:39 
GeneralRe: Yes, but ... Pin
Florian Rappl19-Nov-13 23:53
professionalFlorian Rappl19-Nov-13 23:53 
GeneralRe: Yes, but ... Pin
Meshack Musundi20-Nov-13 0:10
mvaMeshack Musundi20-Nov-13 0:10 
GeneralRe: Yes, but ... Pin
Meshack Musundi20-Nov-13 0:17
mvaMeshack Musundi20-Nov-13 0:17 
GeneralRe: Yes, but ... Pin
Florian Rappl20-Nov-13 0:25
professionalFlorian Rappl20-Nov-13 0:25 
GeneralRe: Yes, but ... Pin
Meshack Musundi20-Nov-13 0:56
mvaMeshack Musundi20-Nov-13 0:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.