For this first recipe, we chose to create a cool user
interaction, in order to provide a better user experience (UX). When
the device is shaken, we want all the text in the text boxes to be
erased. As you know, people are fascinated by features that make a
device's functionality more interesting. In this example, you will use a
data form, but you could take this code and put it, for example, in a
Sudoku game. In that case, creating a new schema when shaking the phone
would be simple and requires only a change of the algorithm inside the
method that resets the text boxes.
1. Problem
You need to detect the shaking of the device along the x axis.
2. Solution
You must use the Accelerometer class, subscribe to the event ReadingChanged, and manage the phone's movements.
3. How It Works
An accelerometer is
an instrument capable of detecting and/or measuring acceleration. The
accelerometer of Windows Phone 7 is accessible thanks to the Accelerometer class, which simply exposes methods to begin (Start) and end (Stop)
the use of the accelerometer, and the event that will notify you that
the values read from the device have changed. A fundamental thing to be
taken into consideration is the scale of values that can be reported by
the instrument along the x-, y-, and z-axis, which have a lower limit of
−2 and an upper limit of 2.
4. The Code
It's useless to tell you one
more time how to create a new project , and so
we'll start directly with the code that interests us, then start
creating a new Silverlight application for Windows Phone.
For the user interface (UI),
you will have only a set of four text boxes that contain information
for Name, Surname, Street, and City:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0" Grid.RowSpan="6">
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="80" />
<RowDefinition Height="80" />
<RowDefinition Height="80" />
<RowDefinition Height="80" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Name="NameTextBox" Text="TextBlock" />
<TextBox Name="SurnameTextBox" Text="TextBlock" Grid.Row="1" />
<TextBox Name="StreetTextBox" Text="TextBlock" Grid.Row="2" />
<TextBox Name="CityTextBox" Text="TextBlock" Grid.Row="3" />
<TextBlock Text="Shake to reset" Grid.Row="4" VerticalAlignment="Center"
HorizontalAlignment="Center"/>
</Grid>
The first class that you must know about is Accelerometer,
which allows you to access the functionality that you need. It's
important to say how the movements are interpreted by the device.
Assuming that you have your Windows Phone in your hand in portrait
position, the movement along the x-axis is considered positive if you
move the phone to the right (and negative if you move the phone to the
left). Movement along the y-axis is relative to moving the phone up (in a
positive direction) and down (in a negative direction). Movement along
the z-axis occurs when you move the phone toward you (positive) and away
from you (negative).
NOTE
The Accelerometer class (which gives you access to accelerometer functionality) is contained in the namespace Microsoft.Device.Sensors. and to use it you must add a reference to the Microsoft.Device.Sensors assembly.
As you can see from the class diagram shown in Figure 6-1, there is an event to be subscribed to (ReadingChanged).
This event is triggered every time the reference values of the
accelerometer change (and based on the accelerometer's high degree of
accuracy, those values will change often).
At the class level, you need to declare these members:
...
Accelerometer accelerometer = null;
//represents the time of the first significant movement
DateTimeOffset movementMoment = new DateTimeOffset();
double firstShakeStep = 0; //represents the value of the first significant movement
...
You will see soon why you need these two members.
Next you will write a private method named initAccelerometer that you will call just after InitializeComponents
in the page constructor. This method will be responsible for
initializing all you need for the accelerometer, such as the
subscription to the ReadingChanged event.
...
private void initAccelerometer()
{
accelerometer = new Accelerometer();
accelerometer.ReadingChanged += new
EventHandler<AccelerometerReadingEventArgs>(Accelerometer_ReadingChanged);
accelerometer.Start(); //this can be done in a button_click
}
...
The preceding code is
self-explanatory, but note that with the last line you actually started
the use of the accelerometer. But let's show in detail what you will do
with this accelerometer, by using the event handler:
void Accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
//put here the code to detect a shake
}
Assuming that by shake
we mean a fast movement, large enough along the x-axis, the code that
allows us to identify a movement of this type makes use of a precise
timestamp, in order to access the actual position (relative to the
starting point) of the phone at all times.
This code is as follows:
...
void Accelerometer_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
//if the movement takes less than 500 milliseconds
if (e.Timestamp.Subtract(movementMoment).Duration().TotalMilliseconds <= 500)
{
if ((e.X <= −1 || e.X >= 1) && (firstShakeStep <= Math.Abs(e.X)))
firstShakeStep = e.X;
if (firstShakeStep != 0)
{
firstShakeStep = 0;
Deployment.Current.Dispatcher.BeginInvoke(() => ResetTextBox());
}
}
movementMoment = e.Timestamp;
}
...
All this code is based
on a mathematical calculation to determine the magnitude of movement
along the x-axis (you can test it moving the phone along the y- and
z-axis). But why do you need to write Deployment.Current.Dispatcher.BeginInvoke(() => ResetTextBox())? It's simple:
() => ResetTextBox() represents a delegate to the ResetTextBox method.
Deployment.Current.Dispatcher.BeginInvoke
is used to execute Action on the same thread of the UI. If you try to
call a method that works on UI elements without this methodology, you
will receive an "Invalid cross-thread access" error.
Finally, you must implement the ResetTextBox method:
...
private void ResetTextBox()
{
this.NameTextBox.Text = "";
this.SurnameTextBox.Text = "";
this.StreetTextBox.Text = "";
this.CityTextBox.Text = "";
}
...
There is something important
to note about this code: With this code, you started to use the
accelerometer, but you never stopped it. That means you will be using
this sensor for the entire time that your application is working. This
is a bad practice because the application will consume too much battery
power. It is becoming increasingly important to offer the best user
experience possible. If the user thinks that the application requires an
excessive use of resources, that user might simply delete it.
5. Usage
In this recipe, you need a
physical device to test the application. Indeed, you cannot use the
emulator to test the accelerometer.
Connect your unlocked device and run
the application from Visual Studio 2010. Make sure that the target
output is set to Windows Phone 7 Device. Write something in the text
boxes, shake the phone, and then check that your form has been erased.