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.
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.
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,
<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.
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
namespace LenovoStriker
{
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 Value
s 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.
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