You will learn to use the captured accelerometer data
to do something more useful: moving the image of a ball as you tilt the
phone left, right, forward, and back. This demo has many uses in
helping you understand how to translate the user input of the
accelerometer data and apply it to UI elements. Figure 1 displays the basic UI of the MoveBallDemo.
1. Creating the MoveBall Project
To set up the CaptureAccelerometerData project, follow the steps you've used for previous examples in this book.
Open Microsoft Visual Studio 2010 Express for Windows Phone on your workstation.
Create a new Windows Phone Application by selecting File =>
New Project in the Visual Studio command menu. Select the Windows Phone
Application template, name the application "MoveBallDemo," and click
OK.
In order to use the accelerometer, add an assembly reference to Microsoft.Devices.Sensors by right-clicking the References folder in Solution Explorer and choose Microsoft.Devices.Sensors from the Add Reference window.
2. Building the User Interface
You will be building the user
interface using the XAML in Visual Studio. For building simple
controls, it is faster to work with the XAML code. Go to Solution
Explorer, open MainPage.xaml, and replace the XAML you find there with the following codes.
2.1. Declaring the UI Resources
The namespaces you see here
are typically declared by default when you first create the Windows
Phone project, and the namespaces like xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" will allow you to add common Windows Phone controls.
<phone:PhoneApplicationPage
x:Class="MoveBallDemo.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
shell:SystemTray.IsVisible="True">
2.2. Building the Main Page and Adding Components
The UI consists of Start and
Stop buttons for stopping and starting the accelerometer and a ball that
moves as the Windows Phone is tilted left, right, forward, and
backward.
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="24,24,0,12">
<TextBlock x:Name="ApplicationTitle" Text="MoveBallDemo"
Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
<Button Content="Start" Height="72"
HorizontalAlignment="Left" x:Name="btnStart"
VerticalAlignment="Top" Width="160"
Click="btnStart_Click" Margin="8,537,0,0"
Grid.Row="1" d:LayoutOverrides="HorizontalAlignment" />
<Button Content="Stop" Height="72"
HorizontalAlignment="Left" x:Name="btnStop"
VerticalAlignment="Top" Width="160"
Click="btnStop_Click" Margin="168,537,0,0"
Grid.Row="1" />
<Canvas x:Name="ContentGrid" Margin="0,8,8,0"
Grid.Row="1" HorizontalAlignment="Right"
Width="472" Height="479" VerticalAlignment="Top">
<Ellipse x:Name="ball" Canvas.Left="126"
Fill="#FF963C3C" HorizontalAlignment="Left"
Height="47" Stroke="Black" StrokeThickness="1"
VerticalAlignment="Top" Width="46"
Canvas.Top="222"/>
</Canvas>
</Grid>
</phone:PhoneApplicationPage>
Once you've loaded the XAML code, you should see the layout shown in Figure 2. Now it's time to animate the ball and add the sound effect by wiring up some events, which you'll do next.
3. Coding the Application
In Solution Explorer, open MainPage.xaml.cs and replace the code there with the following code C# code blocks.
3.1. Specifying the Namespaces
Begin by listing the namespaces the application will use. Notice our inclusion of Microsoft.Devices.Sensors that will allow us to start and stop Windows Phone's accelerometer.
using System;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
using Microsoft.Devices.Sensors;
namespace MoveBallDemo
{
public partial class MainPage : PhoneApplicationPage
{
3.2. Initializing Variables
The variable _ac, an Accelerometer object, will be used to start and stop the sensor, and retrieve x, y, z and time value. Also notice the ReadingChanged
event, which sends the captured accelerometer data to be displayed in
the UI. Also the starting position of the ball is set to the center of
the canvas where the ball is placed.
private Accelerometer _ac;
public MainPage()
{
InitializeComponent();
SupportedOrientations = SupportedPageOrientation.Portrait;
ball.SetValue(Canvas.LeftProperty, ContentGrid.Width / 2);
ball.SetValue(Canvas.TopProperty, ContentGrid.Height / 2);
_ac = new Accelerometer();
_ac.ReadingChanged += new
EventHandler<AccelerometerReadingEventArgs>(ac_ReadingChanged);
}
3.3. Handling Captured Accelerometer Data
Notice that here, as in the
previous demo, you cannot directly change the UI elements upon receiving
the accelerometer data because the accelerometer data comes from a
different thread than the current UI thread. If you try to change the UI
elements directly here, you will get an "Invalid cross-thread access"
error. In order to overcome this problem, you must use the Dispatcher in the current UI thread, as shown in the following code.
private void ac_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => MyReadingChanged(e));
}
3.4. Applying Captured Accelerometer Data to the Ball
The following code
achieves the behavior where if the phone is tilted in vertically with
the display facing toward you, based on the algorithm specified in the
method, the ball will fall straight very fast. But if you tilt the phone
slightly while the display is facing up, the ball will slowly slide to
the direction in which the phone is tilted.
private void MyReadingChanged(AccelerometerReadingEventArgs e)
{
double distanceToTravel = 2;
double accelerationFactor = Math.Abs(e.Z) == 0 ? 0.1 : Math.Abs(e.Z);
double ballX = (double)ball.GetValue(Canvas.LeftProperty) +
distanceToTravel * e.X / accelerationFactor;
double ballY = (double)ball.GetValue(Canvas.TopProperty) -
distanceToTravel * e.Y / accelerationFactor;
if (ballX < 0)
{
ballX = 0;
}
else if (ballX > ContentGrid.Width)
{
ballX = ContentGrid.Width;
}
if (ballY < 0)
{
ballY = 0;
}
else if (ballY > ContentGrid.Height)
{
ballY = ContentGrid.Height;
}
ball.SetValue(Canvas.LeftProperty, ballX);
ball.SetValue(Canvas.TopProperty, ballY);
}
3.5. Adding Start and Stop Button Events
Implement the button event for stopping and starting the accelerometer.
private void btnStart_Click(object sender, RoutedEventArgs e)
{
if (_ac == null)
{
_ac = new Accelerometer();
}
_ac.Start();
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
if (_ac == null)
{
_ac = new Accelerometer();
}
_ac.Stop();
}
}
}
4. Testing the Finished Application
To test the finished application, press F5. Remember to choose to run the application on a Windows Phone 7 device, as shown in Figure 6-15
Once the application runs on the Windows Phone, click the Start button.
Tilt the phone and watch the ball move in the direction the phone is
being tilted.