Windows Phone

Windows Phone 写一个指南针应用程序

Donn Morse

下载代码示例

作为为 Windows 8 的传感器平台文档的 writerresponsible,我想看到许多开发人员尽可能采用我们新的平台。 而且,因为地铁样式应用程序可以使用 XAML 和 C# 编写,Windows Phone 开发商是理想的候选人,为此迁移。 Windows Phone 开发商已经有实验­与 XAML 中,rience 和数量也有经验与传感器 (因为公开加速度计、 指南针、 陀螺仪和 GPS 传感器中的最新的 Windows Phone 版本)。

为帮助我更好地了解 Windows Phone 开发人员和他们的开发平台,决定要写一个简单的罗盘应用去年秋天。 一旦我写的我提交使用 App 集线器的 Windows Phone 市场的免费版本。 自接受,这款应用程序已下载由 Windows Phone 用户从远至瑞士和马来西亚。

本文介绍的应用程序的开发。

指南针应用程序

罗盘应用程序使用的指南针或磁强计,内置到 Windows Phone 的设备。 这个应用程序提供了对真正的北方的标题,以及互惠的标题,当在船上或在一个偏远的地区,用一张地图中的定向越野航行时很有用。 此外,这款应用程序允许用户之间进行切换时 (例如,"090"度) 的数字标题到 alpha 的标题 (例如,"E"东)。 这款应用程序还允许用户锁定当前标题。 当用户需要要保持静止,以便他们可以采取轴承上地图或图表上的特定里程碑或参考点的指针时,这非常有用。

图 1 显示三星焦点上运行的应用程序。 左边的图像显示数值的标题和右边的图像显示 alpha 的标题。

The Running App, Showing a Numeric Heading (Left) and an Alpha Heading (Right)
图 1 显示标题 (左) 的数字和字母标题 (右) 的应用程序运行

设计用户界面

作为开发人员习惯于为 PC 编写的应用程序,最初觉得有限通过减少的屏幕房地产的电话。 然而,这并不是极高。 只是需要给予更多的思想 — — 和审慎考虑 — — 给我新的屏幕尺寸方面的应用程序中的功能。 我罗盘的应用程序有两个屏幕:校准屏幕和主导航屏幕。

校准屏幕的指南针或磁强计、 Windows Phone 设备上安装需要校准后设备通电。 此外,这些传感器可能需要定期重新校准。 为了帮助您以编程方式检测需要进行校正时,该平台支持一种 HeadingAccuracy 属性,您可以使用来检测当前校准。 此外,该平台支持校准激发事件的如果指南针需要校准。

我的应用程序处理该校准事件,这样,反过来,显示一个提示用户手动校准设备通过扫描在电话中一图 8 项议案的校准屏幕 (calibrationStackPanel)。 随着用户席卷手机,当前的精度显示在用红色字体 CalibrationTextBlock 直至取得所需的准确性,如中所示图 2。 一旦返回的准确性是小于或等于 10 °,数字值将被清除并"完成 !",绿色的出现。

The Calibration Screen
图 2 在校准屏幕

相应的代码,它支持标定发现在 compass_Current 中的模块 MainPage.xaml.cs­ValueChanged 事件处理程序,如中所示图 3

图 3 标定指南针

...
else
{
 if (HeadingAccuracy <= 10)
 {
  CalibrationTextBlock.Foreground = 
    new SolidColorBrush(Colors.Green);
  CalibrationTextBlock.Text = "Complete!";
 }
 else
 {
  CalibrationTextBlock.Foreground = 
    new SolidColorBrush(Colors.Red);
  CalibrationTextBlock.Text = 
    HeadingAccuracy.ToString("0.0");
 }
}

一旦取得了所需的精度,被提示用户按下完成按钮,它隐藏在校准屏幕并显示应用程序的主屏幕。

在主屏幕此屏幕显示一个数字或字母标题和互惠的值。 此外,它呈现了面向对真正的北方的罗盘的脸。 最后,让用户能够改变作为输出,主屏幕显示四个控件 (或按钮) 以及锁的标题值和罗盘的脸。

图 4 显示我的应用程序的主屏幕上,MainPage.xaml,因为它在 Visual Studio 中出现。

The App’s Primary Screen in Visual Studio
图 4 应用程序的主屏幕在 Visual Studio 中

在主屏幕中的 UI 元素的大多数是简单 TextBlock 和按钮控件。 文本块标识标题和其互惠的值。 按钮使用户能够控制输出。 然而,有点更多地参与的罗盘的脸。

指南针脸的罗盘的脸由三个组件组成:背景图像、 前景图像和一个较大、 接壤的椭圆,在其中的前景色和背景图像旋转。 背景图像包含一个罗盘、 一条水平线和垂直线上的四个点所对应的字母。 前景图像创建熏玻璃效果。

背景图像在 XAML 中,背景图像被命名为的 CompassFace (稍后在旋转的罗盘的脸的代码中引用此变量的名称):

<Image Height="263" HorizontalAlignment="Left" Margin="91,266,0,0"
  Name="CompassFace" VerticalAlignment="Top" Width="263"  
  Source="/Compass71;component/compass.png" Stretch="None" />

前景图像**的罗盘的脸,EllipseGlass,前景在本身的 XAML 中定义。**熏玻璃效果使用线性渐变画笔进行创建的。 我创建了使用 Microsoft 表达混合 4 此椭圆。 表达混合工具兼容 Visual Studio,并允许您加载您的应用程序的 XAML 和增强用户界面的自定义图形。 图 5 Expression Blend 显示编辑器,它出现时创建的着色的椭圆。

Creating a Shaded Ellipse in Expression Blend
图 5 中表达混合创建一个带阴影的椭圆

我完成表达混合中的椭圆的编辑后,在我的 Visual Studio 项目 XAML 已更新与下面的 XML:

<Ellipse Height="263"  Width="263" x:Name="EllipseGlass" 
  Margin="91,266,102,239" Stroke="Black" StrokeThickness="1">
  <Ellipse.Fill>
    <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
    <GradientStop Color="#A5000000" Offset="0" />
    <GradientStop Color="#BFFFFFFF" Offset="1" />
    </LinearGradientBrush>
  </Ellipse.Fill>

请注意 EllipseGlass (263 x 263 像素) 的尺寸是 compass.png 的尺寸精确匹配。 此外注意到该对象名称,EllipseGlass,引用稍后在代码中执行旋转的罗盘的脸。

指南针的脸边境在一个更大、 白色的椭圆,带有红色边框内旋转罗盘的脸。 此椭圆的 XAML 中定义,命名为 EllipseBorder:

<Ellipse Height="385" HorizontalAlignment="Left" Margin="31,0,0,176"
  Name="EllipseBorder" Stroke="#FFF80D0D" StrokeThickness="2"
  VerticalAlignment="Bottom" Width="385" Fill="White" />

隐藏用户界面代码

代码驻留在文件 MainPage.xaml.cs 中附带的代码下载,和它访问所需的应用程序的命名空间、 初始化传感器、 设置报告的时间间隔和处理应用程序的各种功能:校准、 旋转罗盘的脸,数字和字母输出等之间切换。

访问您的代码中的罗盘写一个罗盘的应用程序 (或任何应用程序访问电话传感器之一) 的第一步是以获取公开的 Microsoft.Devices.Sensors 的命名空间的传感器对象的访问。 这通过下列操作在 MainPage.xaml.cs 中使用指令:

using Microsoft.Devices.Sensors;

一次这个使用指令出现在文件中,可以创建一个罗盘的变量,给我的编程访问在电话中实际的设备:

namespace Compass71
{
  public partial class MainPage : PhoneApplicationPage
  {
    Compass compass = new Compass();

我将使用此变量来启动指南针,停止它,检索当前的航向精度,设置报告的时间间隔,等等。

启动指南针和设置报告频率一旦创建的罗盘的变量,可以调用的方法和对象上的设置属性。 我所调用的第一个方法是开始的方法,它允许我首先从传感器接收数据。 我开始指南针后,设置报告的时间间隔 — — 传感器的更新之间的时间 — — 为 400 毫秒 (请注意 TimeBetweenUpdates 属性,需要 20 ms 的倍数):

compass.TimeBetweenUpdates = 
  TimeSpan.FromMilliseconds(400);  // Must be multiple of 20
compass.Start();

选择 400 毫秒值是由试验和错误。 默认的报告间隔是极短。 如果您尝试运行该应用程序在此默认值,所以经常旋转罗盘的脸似乎是不稳定。

建立罗盘的事件处理程序罗盘的应用程序支持两个事件处理程序:显示校准页 (校准­StackPanel),和另一种呈现当前标题和旋转的罗盘的脸。

定义并建立校准的事件处理程序的校准事件处理程序包含相对较少的代码行。 此代码中所示图 6,完成了两个主要任务:首先,它显示的在 MainPage.xaml 文件中定义的校准屏幕 第二,它将设置为 true 的布尔变量校准。

图 6 校准的事件处理程序

void compass_Calibrate(object sender, 
  CalibrationEventArgs e)
{
  try
  {
    Dispatcher.BeginInvoke(() => 
    { calibrationStackPanel.Visibility =
      Visibility.Visible; 
    });
    calibrating = true;
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message.ToString(), 
       "Error!", MessageBoxButton.OK);
  }
}

因为从后台线程调用此事件处理程序,它没有直接访问到 UI 线程。 所以,若要显示校准屏幕,我需要在调度员对象上调用 BeginInvoke 方法。

布尔变量校准值更改事件处理程序 (compass_CurrentValueChanged) 检查代码中。 当此变量是真的时,我忽略了罗盘,并使用校准的最新数据更新校准屏幕。 当变量是虚假的时我更新罗盘读数,并执行轮换的罗盘的脸。

此事件处理程序被建立与下面的代码行的网页构造函数中:

compass.Calibrate += new EventHandler<CalibrationEventArgs>(compass_Calibrate);

定义并建立 Value-Changed 处理程序 (compass_CurrentValueChanged) 的值更改事件处理程序调用从指南针到达的一种新的阅读每个时间。 根据的校准状态变量,它要么更新校准屏幕或它更新主屏幕。

它更新时的主屏幕,该事件处理程序将执行以下任务:

  • 它计算的真实和互惠的标题,对真正的北方。
  • 它旋转的罗盘的脸。
  • 它将呈现当前和对等标题。

计算标题下面的代码演示如何的事件处理程序检索真北的 SensorReading 对象上使用 TrueHeading 属性的标题:

 

TrueHeading = e.SensorReading.TrueHeading;
  if ((180 <= TrueHeading) && (TrueHeading <= 360))
    ReciprocalHeading = TrueHeading - 180;
  Else
    ReciprocalHeading = TrueHeading + 180;

图 7 演示的事件处理程序如何更新当前和对等标题。

图 7 更新当前和互惠的标题

if (!Alphabetic) // Render numeric heading
{
  HeadingTextBlock.Text = TrueHeading.ToString();
  RecipTextBlock.Text = ReciprocalHeading.ToString();
}
else // Render alpha heading
{
  if (((337 <= TrueHeading) && (TrueHeading < 360)) ||
    ((0 <= TrueHeading) && (TrueHeading < 22)))
  {
    HeadingTextBlock.Text = "N";
    RecipTextBlock.Text = "S";
  }
  else if ((22 <= TrueHeading) && (TrueHeading < 67))
  {
    HeadingTextBlock.Text = "NE";
    RecipTextBlock.Text = "SW";
  }
  else if ((67 <= TrueHeading) && (TrueHeading < 112))
  {
    HeadingTextBlock.Text = "E";
    RecipTextBlock.Text = "W";
  }
  else if ((112 <= TrueHeading) && (TrueHeading < 152))
  {
    HeadingTextBlock.Text = "SE";
    RecipTextBlock.Text = "NW";
  }
  else if ((152 <= TrueHeading) && (TrueHeading < 202))
  {
    HeadingTextBlock.Text = "S";
    RecipTextBlock.Text = "N";
  }
  else if ((202 <= TrueHeading) && (TrueHeading < 247))
  {
    HeadingTextBlock.Text = "SW";
    RecipTextBlock.Text = "NE";
  }
  else if ((247 <= TrueHeading) && (TrueHeading < 292))
  {
    HeadingTextBlock.Text = "W";
    RecipTextBlock.Text = "E";
  }
  else if ((292 <= TrueHeading) && (TrueHeading < 337))
  {
    HeadingTextBlock.Text = "NW";
    RecipTextBlock.Text = "SE";
  }
}

旋转罗盘脸下面的代码段演示了这款应用程序如何旋转这两个椭圆构成的背景与前景的罗盘的脸:

CompassFace.RenderTransformOrigin = new Point(0.5, 0.5);
EllipseGlass.RenderTransformOrigin = new Point(0.5, 0.5);
transform.Angle = 360 - TrueHeading;
CompassFace.RenderTransform = transform;
EllipseGlass.RenderTransform = transform;

变量 CompassFace 对应于包含指南针 (N、 E、 W 和 S) 和横向和纵向线的四个点的背景图像。 变量 EllipseGlass 对应的熏玻璃图层。

我可以应用旋转变换之前,我需要确保变换居中的我会旋转的两个对象。 这是通过调用 RenderTransformOrigin 方法对每个对象和供应的坐标 (0.5、 0.5)。 (有关此方法和其使用有关的详细信息,请参阅 MSDN 库页,"UIElement.RenderTransformOrigin 财产,"在 bit.ly/KIn8Zh。)

一旦我已经为中心转变,我可以计算角度和执行旋转。 我计算角度减去从 360 当前标题。 (这是我刚收到的事件处理程序中的标题)。我申请了这个新角度的变换器属性。

锁定和解锁指南针的锁定和解锁­ing 功能用于室外人使用应用程序导航 (无论是在一条船或手远足径用地图上)。 此功能很简单 ; 它调用 Stop 方法的指南针来锁定在标题上,然后它调用 Start 方法恢复标题检索。

当用户按下 LockButton 时调用 Stop 方法:

private void LockButton_Click(object sender, 
  RoutedEventArgs e)
{
  try
  {
    compass.Stop();
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message.ToString(), 
      "Error!", MessageBoxButton.OK);
  }
}

当用户按下 UnlockButton 时调用 Start 方法:

private void UnlockButton_Click(object sender, 
  RoutedEventArgs e)
{
  try
  {
    compass.Start();
    compass.TimeBetweenUpdates =
      TimeSpan.FromMilliseconds(400);  
      // Must be multiple of 20
  }
  catch (Exception ex)
  {
    MessageBox.Show(ex.Message.ToString(), 
      "Error!", MessageBoxButton.OK);
  }
}

请注意除了重新启动指南针,复位的报告间隔到 400 ms,以确保一致的行为。

Toggling 之间数字和字母标题的字母和数字的标题之间切换的代码由单个布尔变量命名为 Alphabetic 时,如果用户按 AlphaButton 或 NumericButton 设置的控制。 当用户按下 AlphaButton 时,此变量被设置为 True ; 当用户按下 NumericButton,系统会将它设置为 False。

以下是 AlphaButton 单击事件的代码:

private void AlphaButton_Click(
    object sender, RoutedEventArgs e)
  {
    try
    {
      Alphabetic = true;
    }
    catch (Exception ex)
    {
      MessageBox.Show(
         ex.Message.ToString(), "Error!",
        MessageBoxButton.OK);
    }
  }

在 compass_CurrentValueChanged 事件处理程序代码检查 Alphabetic 以确定它是否应该呈现的数字或字母的标题。

支持的光与暗的可见性主题创建应用程序,并提交它的 App 集线器的认证后,我非常惊讶地接收它已失败,因为某些用户界面元素不见光的可见性主题测试时的通知。 (我一直只与黑暗主题,失败了,测试光主题。

若要解决此问题,我的网页构造函数,它检索当前主题,然后设置的用户界面元素 (文本块和按钮),为给定的主题工作的前景颜色添加代码。 如果光主题设置,元素的前景颜色设置为黑色和红色。 如果设置了黑暗的主题,则元素的前景颜色设置为黑暗与光明的灰色。 图 8 显示此代码。

图 8 协调主题和颜色

Visibility isLight = (Visibility)Resources["PhoneLightThemeVisibility"]; // For light theme
if (isLight == System.Windows.Visibility.Visible) // Light theme enabled
{
  // Constructor technique
  SolidColorBrush scb = new SolidColorBrush(Colors.Black);
  SolidColorBrush scb2 = new SolidColorBrush(Colors.Red);
  RecipLabelTextBlock.Foreground = scb;
  HeadingLabelTextBlock.Foreground = scb;
  RecipTextBlock.Foreground = scb2;
  HeadingTextBlock.Foreground = scb2;
  LockButton.Foreground = scb;
  UnlockButton.Foreground = scb;
  AlphaButton.Foreground = scb;
  NumericButton.Foreground = scb;
}
else // Dark color scheme is selected—set text colors accordingly
{
  // Constructor technique
  SolidColorBrush scb = new SolidColorBrush(Colors.DarkGray);
  SolidColorBrush scb2 = new SolidColorBrush(Colors.LightGray);
  RecipLabelTextBlock.Foreground = scb;
  HeadingLabelTextBlock.Foreground = scb;
  RecipTextBlock.Foreground = scb2;
  HeadingTextBlock.Foreground = scb2;
  LockButton.Foreground = scb;
  UnlockButton.Foreground = scb;
  AlphaButton.Foreground = scb;
  NumericButton.Foreground = scb;
}

乐趣和有价值的

此应用程序的创建是有很多乐趣,和它也有价值。 曾经在 Windows Phone 平台上的传感器,现在我更清楚地了解这一平台和传感器支持之间在 Windows 8 的差异。 但我感触最大的相似之处。 我的预感是如果你是 Windows Phone 的开发人员花了与传感器的命名空间的任何时间,你去找迁移到 Windows 8 极其简单。 而且,在 Windows 8,你会发现其他传感器测斜仪、 定位传感器和简单定位传感器等。 (方向传感器是多个传感器,它返回一个四元数或您可以使用来控制复杂的游戏的旋转矩阵的融合。 简单定位传感器可以检测到您的设备是否在纵向或横向模式中,以及仰面朝下。)

所有这些传感器所带来的发展机遇是令人兴奋和期待看到我们创造性的开发人员社区可以运用他们的想象力的方式。

Donn Morse 是一个高级编程作家在微软 Windows 团队在过去的几年一直致力于传感器平台,从到驱动程序的应用方面。他是激情,和着迷传感器和它们的使用。

由于下面的技术专家,检讨这篇文章:Jason Scott