This post is about introducing you Windows Phones Accelerometer (I have taken following contents from
Charles Petzold's book "
Programming Windows Phone 7 - Special Excerpt 2". The contents are Copyright of Microsoft and is used only for informational purpose). You can get more detailed information about using Accelerometer from above mentioned book, complete book is still to be released later this year.
Windows Phones contain an accelerometer — a small hardware device that essentially measures force, which elementary physics tells us is proportional to acceleration. When the phone is held still, the accelerometer responds to the force of gravity, so the accelerometer can tell your application the direction of the Earth relative to the phone. It is convenient to represent the accelerometer output as a vector in three-dimensional space. Vectors are commonly written in boldface, so the acceleration vector can be symbolized as (x, y, z). XNA defines a three-dimensional vector type; Silverlight does not.
While a three-dimensional point (x, y, z) indicates a particular location in space, the vector (x, y, z) encapsulates instead a direction and a magnitude. Obviously the point and the vector are related: The direction of the vector (x, y, z) is the direction from the point (0, 0, 0) to the point(x, y, z). But the vector (x, y, z) is definitely not the line from (0, 0, 0) to (x, y, z). It’s only the direction of that line. The magnitude of the vector (x, y, z) is calculable from the three-dimensional form of the Pythagorean Theorem:
Magnitude = Math.sqrt( Math.pow(x,2), Math.pow(y,2), Math.pow(z,2) )
where:
Math.sqrt() function returns the square root of a number
Math.pow() function returns a number raised to a power
For working with the accelerometer, you can imagine the phone as defining a threedimensional coordinate system. No matter how the phone is oriented, the positive Y axis points from the bottom of the phone (with the buttons) to the top, the positive X axis points from left to right.
When the phone is still, the accelerometer vector points towards the Earth. The magnitude is 1, meaning 1 g, which is the force of gravity on the earth's surface. When holding your phone in the upright position, the acceleration vector is (0, –1, 0), that is, straight down.
So lets start, To use the accelerometer, you’ll need a reference to the
Microsoft.Devices.Sensors
library, and a using directive for the
Microsoft.Devices.Sensors
namespace. In
WMAppManifest.xml
, you need
<Capability Name="ID_CAP_SENSORS">
You create an instance of the Accelerometer class, set an event handler for the
ReadingChanging event, and call Start. And then it gets a little tricky. Let’s take a look at a project named
SilverlightAccelerometer
project that simply displays the current reading in its content grid. A centered
TextBlock
is defined in the XAML.
<Grid x:Name="ContentGrid" Grid.Row="1">
<TextBlock Name="txtblk"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
This is a program that will display the accelerometer vector throughout its lifetime, so it creates the Accelerometer class in its constructor and calls Start:
public MainPage()
{
InitializeComponent();
Accelerometer acc = new Accelerometer();
acc.ReadingChanged += OnAccelerometerReadingChanged;
try
{
acc.Start();
}
catch (Exception exc)
{
txtblk.Text = exc.Message;
}
}
The documentation warns that calling Start might raise an exception, so the program protects itself against that eventuality. These user-interface objects are not thread safe; they are not built to be accessed simultaneously from multiple threads. For this reason, Silverlight will not allow you to access a user-interface object from a non-UI thread.
This means that the
OnAccelerometerReadingChanged
method cannot directly access the TextBlock element to set a new value to its Text property. Fortunately, there’s a solution involving a class named Dispatcher defined in the
System.Windows.Threading
namespace. Through the
Dispatcher
class, you can post jobs from
a non-UI thread on a queue where they are later executed by the UI thread.
The Dispatcher class defines a method named
CheckAccess
that returns true if you can access a particular user interface object from the current thread. (The
CheckAccess
method is also duplicated by
DependencyObject
itself.) If an object can’t be accessed from the current thread, then Dispatcher provides two versions of a method named
Invoke
that you use to post the job to the UI thread.
The verbose version requires a delegate and a method defined in accordance with that delegate. The delegate (and method) should have no return value, but as many arguments as you need to do the job, in this case setting a string to the Text property of a TextBlock:
delegate void SetTextBlockTextDelegate(TextBlock txtblk, string text);
void SetTextBlockText(TextBlock txtblk, string text)
{
txtblk.Text = text;
}
The
OnAccelerometerReadingChanged
is responsible for calling
SetTextBlockText
. It first makes use of
CheckAccess
to see if it can just call the
SetTextBlockText
method directly. If not, then the handler calls the
BeginInvoke
method. The first argument is an instantiation of the delegate with the
SetTextBlockText
method; this is followed by all the arguments that
SetTextBlockText
requires:
void OnAccelerometerReadingChanged(object sender, AccelerometerReadingEventArgs args)
{
string str = String.Format("X = {0:F2}\n" +
"Y = {1:F2}\n" +
"Z = {2:F2}\n\n" +
"Magnitude = {3:F2}\n\n" +
"{4}",
args.X, args.Y, args.Z,
Math.Sqrt(args.X * args.X + args.Y * args.Y +
args.Z * args.Z),
args.Timestamp);
if (txtblk.CheckAccess())
{
SetTextBlockText(txtblk, str);
}
else
{
txtblk.Dispatcher.BeginInvoke(new
SetTextBlockTextDelegate(SetTextBlockText),
txtblk, str);
}
}
This is not too bad, but the need for the code to jump across threads has necessitated an additional method and a delegate. Is there a way to do the whole job right in the event handler?
Yes! The
BeginInvoke
method has an overload that accepts an Action delegate, which defines a method that has no return value and no arguments. You can create an anonymous method right in the
BeginInvoke
call. The complete code following the creation of the string object looks like this:
if (txtblk.CheckAccess())
{
txtblk.Text = str;
}
else
{
txtblk.Dispatcher.BeginInvoke(delegate()
{
txtblk.Text = str;
});
}
The anonymous method begins with the keyword delegate and concludes with the curly
brace following the method body. The empty parentheses following the delegate keyword are not required.
The Windows Phone 7 emulator doesn’t contain any actual accelerometer, so it always reports a value of (0, 0, –1), which indicates the phone is lying on a flat surface: