65.9K
CodeProject 正在变化。 阅读更多。
Home

设计一个简单的Windows Phone 7应用程序

2010年11月2日

CPOL

11分钟阅读

viewsIcon

28232

了解如何为 Windows Phone 7 构建典型的列表/详细信息应用程序。本文介绍了一个简单的客户列表编辑器,该编辑器利用了各种数据编辑器——所有这些都借助 Resco 的 MobileLight Toolkit 构建。

了解如何为 Windows Phone 7 构建典型的列表/详细信息应用程序。本文介绍了一个简单的客户列表编辑器,该编辑器利用了各种数据编辑器——所有这些都借助 Resco 的 MobileLight Toolkit 构建。

引言

Windows Phone 7 (WP7) 编程结合了几种技术

  • .NET (WP7 使用完整 .NET 类库的一个子集)
  • C# (目前不支持其他语言)
  • Silverlight for WP7。(桌面 SVL 的简化版,而 SVL 本身又是 WPF 的简化版。)

对于来自 (例如) Windows Mobile 编程的程序员来说,掌握这些技术是一个真正的问题。本文将介绍如何使用 Resco 的 MobileLight Toolkit 为 Windows Phone 7 构建典型的列表/详细信息应用程序。

必备组件

目标受众是熟悉 Silverlight 的 C# .NET 程序员。至少,读者应该了解基本的 Silverlight 控件和布局。

您需要安装 Windows Phone Developer Tools。(需要 Vista 或 W7、2GB RAM、DirectX 10+) 安装包括

  • Visual Studio 2010 Express for WP,
  • 模拟器 (不需要真实手机设备)。

此外,您应该从 http://www.resco.net/developer/mobilelighttoolkit 安装 Resco MobileLight Toolkit。

该工具包包含一组有用的控件,可以简化 Windows Phone 7 编程。附带的 SampleApplication 提供了一个模板,可以用来构建广泛的典型业务类应用程序,而无需深入研究高级 Silverlight 主题。

尽管 SampleApplication 处理更复杂的概念——绑定、样式和模板——但所有这些都可以从注释中理解。

应用程序预览

上图截图演示了应用程序的功能

  • 主屏幕显示客户列表。
  • 选定的客户显示方式不同,因为它显示了提供呼叫、电子邮件和编辑操作的按钮。
  • 底部有一个 [+] 按钮用于创建新客户。
  • 客户表单包含各种客户属性编辑器以及保存/删除按钮。

特殊编辑器——组合框和日期时间选择器显示在下面的截图中。

本文档的其余部分将介绍此应用程序的源代码。我们建议您安装 Resco MobileLight Toolkit 并打开 SampleApplication 项目。

应用程序描述

该应用程序用作客户列表的编辑器。它显示所有客户的列表,并允许您添加/删除/编辑单个客户。

代码包括

  • Data
    • Customer.cs 包含 Customer 类及相关类。
    • Customer 是一个简单的类,包含一组公共属性。它实现了 INotifyPropertyChanged 接口,以便 UI 控件可以响应数据更改。
    • Customers 类提供了一个全局的客户列表。它还负责数据的加载和存储。
    • Customers.xml 是一个示例客户列表,采用 XML 格式,用于应用程序初始化。
  • 图像
  • 三个类——AppMainPageDetailsPage,即 WP7 应用程序的标准结构 (1 个应用程序对象 + 2 个页面)。
    • 请注意,每个类实际上由 2 个文件表示——一个用于 GUI (xaml 文件),一个用于相应的代码 (xaml.cs 文件)。

App 类

App.xamlApp.xaml.cs 的代码由 Visual Studio 生成。唯一添加的是这一行

 Customers.Save();

Application_Deactivated/Application_Closing 处理程序中。(换句话说,我们告诉 Customers 类是时候关闭了) 就是这样。那么,您可能会问,应用程序是如何实际启动的?这是在 WMAppManifest.xml 文件中通过后台完成的。此文件由 Microsoft Visual Studio 组织,包含一些标准内容 (背景、应用程序标题) 和启动页——在本例中为 MainPage

MainPage 类

除了某些初始化/终止内容之外,WP7 应用程序的编程从这里开始。MainPage.xaml 定义了 UI——标题、底部栏 (带 [New] 按钮) 和最重要的部分——AdvancedList 控件。MainPage.xaml.cs 包含

  • 由 Microsoft Visual Studio 生成的标准构造函数。(InitializeComponent() 是在运行时读取 xaml 文件的命令。)
  • OnNavigatedTo 处理程序。WP7 应用程序由页面组成,当页面变为活动状态时,框架会调用此处理程序。在本例中,我们执行一件事——设置 DataContext,它是 Customers 类的一个实例。然后,DataContext 会被页面控件继承。在本例中,是列表使用继承的 DataContext 来显示 Customers.Items 集合。
  • 文件的其余部分包含事件处理程序。启动电子邮件或电话呼叫的事件处理程序很简单 (参见代码),而导航将在其他地方描述。

导航工作原理

WP7 应用程序具有标准的结构,因为它们总是包含以下组件

  • 应用程序对象负责应用程序的生命周期——启动/终止、激活/停用。
  • 页面,本质上是表单。(派生自 PhoneApplicationPage 类的对象。)
  • 页面之间的导航。

导航概念很简单

  • 系统导航到第一个页面。(在 WMAppManifest.xml 中定义)
  • 应用程序可以使用 NavigationService.Navigate() 调用导航到另一个页面。
  • 用户按下后退键或开始按钮。(返回上一页或退出应用程序。)

每个页面 (PhoneApplicationPage 派生类) 都有两个与导航相关的回调

  • OnNavigatedTo — 页面激活时调用。
  • OnNavigatedFrom — 页面停用时调用。

最后要提到的是这些方法中使用的参数。在这方面,有两点很重要

  • 我们导航到 xaml 文件。只要我们讨论的是应用程序内的 xaml 文件,所使用的文件名就是相对于项目文件夹的完整路径。

因此,我们可以导航到例如 "/SubFolder/MyFile.xaml"

  • 我们需要传递一些参数。Microsoft 决定采用浏览器使用的 Uri 技术。

Uri 通常具有 <xaml_path>[?<query>] 的形式,其中

query:=  key1=value1&key2=value2&...                  //  or key1=value1;key2=value2;...

在本例中,导航对话如下所示

MainPage 发出命令

Navigate(  "/DetailsPage.xaml?selectedItem=12" )           //  Open the item #12
Navigate(  "/DetailsPage.xaml?newItem=true" )              //  Create new item\
DetailsPage reacts in  OnNavigatedTo():

系统将查询传递给页面属性 NavigationContext。其成员 QueryString 包含传递的参数的键/值字典。(这可以省去解析相当复杂的查询格式。)

在本例中,我们只分析单个键/值对,即 [selectedItem,12] 或 [newItem,true]。

AdvancedList 如何工作

大部分列表编程都在 Xaml 文件中完成。这是代码方案

// MainPage.xaml
<r:AdvancedList  x:Name="MainListBox" ItemsSource="{Binding Items}" ...
    NormalTemplate="normal"  SelectedTemplate="selected">
<r:AdvancedList.DataTemplates>
<DataTemplate  x:Name="normal"> ... </DataTemplate>
<DataTemplate  x:Name="selected"> ... </DataTemplate>
</r:AdvancedList.DataTemplates>
</r:AdvancedList>

 

// MainPage.xaml.cs
public  partial class MainPage : PhoneApplicationPage {
protected override void  OnNavigatedTo(NavigationEventArgs e) {
//...
if (DataContext  == null)
DataContext  = Customers.Instance;
}
private void  btnCall_Click(object sender, RoutedEventArgs e) {
//...
var customer =  Customers.Instance.Items[MainListBox.SelectedIndex];
}
}

让我们分析一下代码

  • AdvancedList Xml 元素的名字属性定义了实例 AdvancedList MainListBox;然后,在 btnCall_Click 处理程序中使用此对象来获取选定的客户。
  • DataContext 在 .cs 文件中设置为 Customers.Instance。如果您现在意识到 AdvancedListItemsSource 绑定到 DataContext 属性 Items,那么就很清楚列表显示的是 Customers.Items。此外,它是一个 ObservableCollection,即绑定是“实时”的。这意味着,如果应用程序的其余部分添加/删除/更改客户,列表将自动响应。(请注意,还有其他方法可以提供 ListBox 项目,但这里描述的方法代表了主流用法。)
  • 最后一个问题是如何显示列表项。这由 DataTemplate 定义,稍后将讨论。暂时请注意,有两种模板——一种用于选定项,另一种用于未选定项。

数据模板

模板用于显示列表项,这些列表项 (在本例中) 是 Customer 对象。

这是未选定项的模板

<DataTemplate  x:Name="normal">
<Grid>
		// Define grid 2x2
		<Grid.RowDefinitions>  <RowDefinition/>  <RowDefinition/>  
                   </Grid.RowDefinitions>
		<Grid.ColumnDefinitions>  <ColumnDefinition/>  <ColumnDefinition/> 
                   </Grid.ColumnDefinitions>
		
		// First grid row shows the value  Customer.Name
		<TextBlock  Grid.ColumnSpan="2" Text="{Binding Name}" 
		Style="{StaticResource PhoneTextExtraLargeStyle}"/>
		
		// 2nd row displays Customer properties  EMailAddress1 and Telephone1
		<TextBlock Grid.Row="1"  Grid.Column="0" Text="{Binding EMailAddress1}" 
		Style="{StaticResource PhoneTextSubtleStyle}"/>
		<TextBlock Grid.Row="1"  Grid.Column="1" Text="{Binding Telephone1}" 
		Style="{StaticResource PhoneTextSubtleStyle}"/>
</Grid>
</DataTemplate>

还请注意,我们不直接指定文本属性 (字体、高度等),而是依赖预定义的 WP7 样式。优点是应用程序将与用户选择的手机主题保持一致。

选定项的模板看起来很相似——它显示与上面相同的项,并添加 3 个按钮——[call]、[email] 和 [edit]。这些按钮由 .cs 文件中的处理程序 (如 btnCall_Click) 处理。

如果您查看 AdvancedList 源代码,您会发现动态模板的处理是该类添加的主要功能。换句话说,AdvancedList 添加了 TemplateSelector——这是 WPF 中存在但 Silverlight 中省略的一项功能。

如何实现动态模板

SampleApplication 依赖于 AdvancedList 的功能,该功能允许为选定/未选定项使用不同的模板。前者包含按钮,而后者仅显示 Customer 属性。

一个合乎逻辑的问题是,我们是否可以使用一个单一的动态模板来根据选择显示/隐藏按钮。换句话说,我们需要一种方法将按钮的可见性绑定到 IsSelected 属性。

不幸的是,Silverlight (与 WPF 不同) 不能绑定到 ListBoxItem。(IsSelectedListBoxItem 的一个属性!)

然而,AdvancedList 有一个解决方案

Customer 对象有一个合适的属性,碰巧使用了相同的名称——IsSelected。如果您将 AdvancedList ItemIsSelectedPath 属性设置为 "IsSelected",那么列表选择将绑定到 Customer.IsSelected 属性。

现在我们可以制作动态模板

<r:AdvancedList ItemsSource="{Binding  Items}"  ...  ItemIsSelectedPath="IsSelected">
<r:AdvancedList.Resources> 
		<r:BooleanToVisibilityConverter  x:Key="MyConverter"/> 
		</r:AdvancedList.Resources> 
		...
		<DataTemplate>
		<Button ... Visibility=
                       "{Binding  IsSelected, Converter={StaticResource MyConverter}}"/>
		...
		</DataTemplate>
</r:AdvancedList>

绑定依赖于转换器。这是因为 IsSelectedVisibility 属性之间存在类型转换。

备注:您可以类似地将 ListBoxItem.IsEnabled 属性绑定到合适的 Customer 属性。

DetailView 如何工作

DetailsPage 用作 Customer 对象的查看器/编辑器。我们已经知道这个表单是通过导航启动的。如果您查看 OnNavigatedTo() 代码,您会发现它的目的是将 DataContext 设置为合适的 Customer 对象 (m_customer)。

.cs 文件处理的事情不多——它只执行最终操作,即保存或放弃更改。

大部分工作都在 Xaml 文件中完成,但即使是那个文件在概念上也很简单

  • 它们只是单独控件的定义,这些控件已正确绑定到 DataContext (即 Customer 对象) 属性。
  • 此外,我们还有标题和 ApplicationBar,其中包含两个按钮——[Save] 和 [Delete]。

代码中最重要的一部分是 DetailView 元素,其中包含各种 DetailViewItems 的列表。项目数量不限;如果需要,DetailView 将会自动滚动。

典型的项目定义如下所示

<r:DetailItemTextBox  Label="Name" DataMember="Name" />

正如您可能猜到的,这是一个 (带标签的) 文本框,用于编辑 Customer.Name 属性。文本框占据整个屏幕宽度,标签显示在其正上方。(所有这些都可以覆盖。)

只有一个更复杂的项目——组合框

<r:DetailItemComboBox  Label="Company Size" DataMember="Size"
ItemsSource="{Binding  Source={StaticResource CustomerSizesKey}}" 
ValueMemberPath="Id"   DisplayMemberPath="Label"/>

前两个属性 (LabelDataMember) 是标准的,即组合框编辑属性

 int  Customer.Size;

其余属性定义了组合框打开时显示的列表内容

  • ItemsSource 提供项目列表。在本例中,它是通过静态资源定义的 CustomerSizes 对象,即 CustomerSize 对象的列表。有关实现细节,请参阅 xaml 代码。
  • DisplayMemberPath 属性告诉列表显示 CustomerSize.Label 属性。
  • ValueMemberPath 属性告诉列表返回 CustomerSize.Id 属性。
  • DetailItemComboBox 将返回的值绑定到 Customer.Size 属性。

随着您对 WP7 编程的熟悉,您会发现设置 DetailView 的其他方法。例如,请参阅 DetailView.xaml 中的注释,其中讨论了设置组合框的其他方法。另请注意,您不仅限于将 DetailViewItem 放置在 DetailView 中。您也可以使用任何其他控件——只需注意正确的绑定即可。

数据去向

Customers.xml 包含首次启动应用程序时使用的数据。

一旦您编辑了数据,它就会被保存到 WP7 的隔离存储中。不幸的是,这意味着其他应用程序无法使用这些数据。

如果您——作为程序员——想查看原始保存的数据,请执行 Customers.Instance() 方法的源代码中描述的技巧。

保存数据的格式依赖于旧的、可靠的 XmlSerializer。它涵盖了大多数常见场景,尽管对于更复杂的类型可能会出现问题,因为 Silverlight 不支持自定义序列化 (ISerializable 接口、Serializable 属性)。

或者,您可以使用 Silverlight 消息传递原生使用的 DataContractSerializer (DCS)。

尽管 DCS 较新,但它并不明显优于 XmlSerializer。(DCS 速度稍快,但产生的负载更大。)

我们的建议是先使用 XmlSerializer,只有当前者不支持所有数据格式时才切换到 DCS。

目前,WP7 上唯一有意义的数据共享方式是 Web 服务。但是,这个话题远远超出了本文档的范围,这里不进行讨论。

下一步

Resco 是一家在移动编程领域拥有悠久传统的公司,涵盖终端用户应用程序和开发人员工具。该工具包——在其当前发布版本中——只是第一个版本。在不久的将来,现有控件将得到增强,并将添加新控件。我们还计划发布一系列教程,以帮助您快速入门 Windows Phone 7 编程。

欢迎任何反馈——无论是在此处还是直接在 Resco 论坛上。

关于 Resco MobileLight Toolkit

Resco MobileLight Toolkit 是一套基于 Microsoft Silverlight 技术的 UI 控件,面向 Windows Phone 7 开发。该套件包括一个列表控件、输入表单控件以及日期/时间选择器控件,并将持续添加更多控件。

更多信息:http://www.resco.net/developer/mobilelighttoolkit/overview.aspx

© . All rights reserved.