Windows Phone 7 的 Netflix 浏览器 - 第 2 部分






4.99/5 (35投票s)
了解如何使用 Pivot 和 Panorama 控件、页面导航、OData 等!

我创建了一个应用程序的视频。
目录
引言
本文是《Windows Phone 7 上的 Netflix 浏览器》文章的第二部分,也是最后一部分。 第一部分侧重于通用地探索 Pivot 和 Panorama 控件,并演示了演示应用程序演练的初始步骤。我们学习了如何将控件添加到项目中,以及如何使用 Pivot 和 Panorama 模板。在这一部分,我们将继续进行演示应用程序的演练,特别是我们将研究 OData 以及如何在 Windows Phone 应用程序中消耗 OData。我们还将探索 Silverlight Toolkit 的 WrapPanel
控件、页面导航和进度条。
演示应用程序概述
演示应用程序是一个 Panorama 应用程序,允许用户浏览 Netflix 数据。在顶层,用户会看到一个电影类型列表、新上映电影和评分最高的电影。然后,用户可以深入查看每部电影以获取电影详细信息。从电影类型列表中选择一个电影类型会将用户带到一个 Pivot 页面,该页面列出所选电影类型的电影,并以三种不同的方式呈现数据:该电影类型的全部电影、按年份排序以及按平均评分排序。理想情况下,我希望看到全部电影类型的列表只显示顶级电影类型,而不是非常长的电影类型和子电影类型列表;但没有简单的方法可以查询这些数据。
将涵盖的内容
必备组件
- Windows Vista 或 Windows 7
- Windows Phone 7 开发人员工具,包括 Visual Studio Express 2010 for Windows Phone、Windows Phone Emulator、Expression Blend 4 for Windows Phone 和 XNA Game Studio 4.0。
- 仅当您尚未安装 Visual Studio Express 2010 和 Expression Blend 4 时,才会安装它们。
- 如果您之前安装了 Beta 工具,则必须在安装最终工具之前卸载它。请注意,安装可能需要一些时间;我的安装花费了一个多小时。让安装程序运行,它最终会完成。
- Silverlight for Windows Phone Toolkit
- OData Client Library for Windows Phone 7(请注意,这是在 PDC 2010 上宣布的生产就绪版本;在第 1 部分中,我们使用的是预览版本)
- CodePlex 网站上有三个下载项。对于此演示,我们只需要ODataClient_BinariesAndCodeGenToolForWinPhone.zip。它包含 OData 客户端程序集和用于生成代理类的 Datasvcutil 代码生成工具。
OData
演示应用程序使用 Open Data Protocol (OData) 公开的 Netflix 数据。关于 OData,odata.org 上有很多信息,但我喜欢 Shayne Burgess 对 OData 的解释,因此我将其粘贴在下面。
简而言之,OData 是一种基于资源的 Web 协议,用于查询和更新数据。OData 使用 HTTP 动词(PUT、POST、UPDATE 和 DELETE)在资源上定义操作,并使用标准的 URI 语法标识这些资源。数据通过 HTTP 使用 AtomPub 或 JSON 标准进行传输。对于 AtomPub,OData 协议定义了标准的一些约定,以支持查询和架构信息的交换。(来源:MSDN Magazine,2010 年 6 月刊)
OData 协议能够访问来自广泛客户端的信息。目前,已有适用于 Windows Phone 7、iPhone、Silverlight 4、PHP、AJAX/Javascript、Ruby 或 Java 的客户端库。此处可以看到完整列表。在演示中,我们使用 OData Client Library for Windows Phone 7。OData 服务可以在任何支持 HTTP 的服务器上实现。.NET 实现通过 WCF Data Services 提供支持。WCF Data Services 是 .NET Framework 的一个组件,以前称为 ADO.Net Data Services(代号 Astoria)。WCF Data Services 提供了一个创建 OData Web 服务的框架,并包含一组客户端库(一个用于通用的 .NET Framework 客户端应用程序,一个用于 Silverlight 应用程序),用于构建消耗 OData Feed 的客户端。
使用 OData 协议公开其数据的服务称为 OData 生产者,而消耗使用 OData 协议公开的数据的客户端称为消费者。odata.org 网站提供了当前生产者和消费者的列表。在生产者中,有 SharePoint 2010、Windows Azure Storage 或 IBM WebSphere 等应用程序,以及演示应用程序中使用的 Netflix Data 服务等多个实时 OData 服务。例如,消费者列表包括 Excel 2010。Microsoft 已根据 Open Specification Promise (OSP) 发布了 OData 规范,这意味着任何人都可以自由构建 OData 客户端和服务。有关如何创建 OData 服务的资源,例如,您可以查看 Scott Hanselman 关于为 StackOverflow 创建自定义 API 的帖子。有关如何在 Windows Phone 应用程序中消耗 OData 的示例,请参阅下一节。
在 WP7 应用程序中消耗 OData
在 Silverlight 项目和 Visual Studio 2010 中,要开始消耗 OData,只需添加服务引用并指定 OData 服务 URL 即可,如图 1 所示。然后,Visual Studio 会自动添加对System.Data.Services.Client.dll 程序集的引用,创建一个用于与数据服务交互的客户端上下文类,并生成一组代表服务公开的类型的代理类。
Windows Phone 应用程序缺少 OData 的“添加服务引用”功能,但正如您将看到的,只需几个简单的步骤,您就可以在 Windows Phone 应用程序中开始使用 WCF Data Services。
- 从 OData Client Library for Windows Phone 7 添加对System.Data.Services.Client.dll 程序集的引用(下载链接请参见先决条件)。
- 在 Visual Studio 的解决方案资源管理器中,右键单击“引用”并选择“添加引用”。浏览到保存 OData Client Library for Windows Phone 7 的位置,然后选择System.Data.Services.Client.dll 程序集。
- 运行 Datasvcutil 工具以生成数据服务类。
- 使用更新版本的 OData Client Library for Windows Phone 7,我们需要使用更新版本的 DataSvcUtil 工具。因此,如果您使用了客户端库的初始版本(正如我在第 1 部分中所做的那样),您将需要重新生成服务类。更新的 DataSvcUtil 工具位于从 Codeplex 下载的ODataClient_BinariesAndCodeGenToolForWinPhone.zip 文件中(请参阅先决条件)。我发现使用更新工具的最佳方法是在解压ODataClient_BinariesAndCodeGenToolForWinPhone.zip 文件内容到某个目录中生成类,然后手动将生成的类复制到项目中。
- 打开 Visual Studio 命令提示符,并切换到您解压ODataClient_BinariesAndCodeGenToolForWinPhone.zip 文件内容到某个目录。然后使用以下命令生成 Netflix 服务类:
DataSvcUtil.exe /uri:http://odata.netflix.com/Catalog/ /out:NetflixModel.cs /Version:2.0 /DataServiceCollection
- 生成的类包含一个名为
NetflixCatalog
的上下文类,以及 Netflix 服务公开的每种类型的客户端代理类。由于我们在上面的命令中使用了/DataServiceCollection 选项,因此生成的代理类实现了INotifyPropertyChanged
接口。 - 将生成的类复制到您的项目中,并将其包含在项目中。
- 在演示中,我创建了一个名为Services 的文件夹,并将生成的类复制到其中。
- 在 Visual Studio 的解决方案资源管理器中选择“显示所有文件”按钮,右键单击新生成的类,然后选择“包含到项目”选项。
处理 OData
设置好项目后,我们就可以开始与 Netflix OData 服务进行交互了。在演示中,我们在 MainViewModel
类中进行此操作。
首先,我们创建允许我们与 Netflix Catalog 交互的上下文。
using alias=NetflixCatalog.Model;
.
.
.
readonly alias.NetflixCatalog context;
public MainViewModel()
{
// Instantiate the context.
context = new alias.NetflixCatalog(new Uri("http://odata.netflix.com/Catalog/",
UriKind.Absolute));
}
您可能会对上面摘录中我使用的别名感到惊讶。我选择此作为解决名称冲突的变通方法。因为,不幸的是,当新的 DataSvcUtil 工具生成 Netflix 服务类时,它会分配 NetflixCatalog.Model 命名空间,这与工具分配给上下文类的 NetflixCatalog 名称发生冲突。
然后我们继续查询服务。回想一下,Panorama 有三个部分:一个用于显示所有电影类型的列表,一个用于新上映电影,最后一个用于热门电影。为此,我创建了三个方法:LoadGenres
、LoadNewTitles
和 LoadTopTitles
,在这些方法中,我构造一个查询来检索所需的 Netflix 数据,并将查询结果加载到一个集合中。
在我们查看这些方法之前,让我们先看看集合成员是如何声明的。正如您所见,我们使用的是 DataServiceCollection
。DataServiceCollection
是一个 ObservableCollection
,它使得处理 WCF Data Services 非常方便。
DataServiceCollection<Genre> genres;
DataServiceCollection<Title> newTitles;
DataServiceCollection<Title> topTitles;
Netflix API 和本文第 1 部分中使用的 OData Client Library for Windows Phone 7 都处于预览模式。Netflix API 仍处于预览模式;但是,OData Client Library for Windows Phone 7 的更新的、生产就绪的版本已经发布。更新的主要变化之一是新版本不再支持 LINQ。预览版本提供了一些 LINQ 支持,本文第 1 部分中的演示应用程序使用了 LINQ 来构造查询。这在新库中将不再有效。因此,我们需要用基于 URI 的查询替换 LINQ 查询。请注意,LINQ 支持可能会在将来的版本中启用。最初,LoagGenres
方法如下所示:
public void LoadGenres()
{
//Instantiate genres
genres = new DataServiceCollection<genre>();
IQueryable<genre> query = from g in context.Genres
select g;
// Asynchronously load the result of the query.
genres.LoadAsync(query);
genres.LoadCompleted += (sender, args) =>
{
if (args.Error != null)
{
Debug.WriteLine("Requesting titles failed. "
+ args.Error.Message);
}
else
{
IsDataLoaded = true;
}
};
}
正如您在上面看到的,我们首先实例化 genres DataServiceCollection
。然后,我们构造一个 LINQ 查询(这是我们将要替换的部分)。DataServiceCollection
有一个 LoadAsync
方法,该方法处理 Silverlight 中网络调用的异步回调,并将查询结果加载到集合中。我们使用 LoadAsync
方法将查询结果加载到我们的 genres 集合中。该方法以一个处理异步回调的 LoadCompleted
事件的事件处理程序结束。
现在我们需要用 URI 查询替换 LINQ 查询。要了解如何构造 URI 查询,我们可以查看 Netflix 网站上的示例查询和指南。或者,因为我们仍然有 LINQ 查询,并且 LINQ 查询会被解释为 URI,所以我们可以在 Visual Studio 中调试时简单地使用“添加监视”来提取查询值。我们也可以使用 Fiddler 等工具来提取正在发送到 Netflix 服务的 URI。
对于 LoadGenres
方法中的电影类型列表,URI 查询非常简单:
http://odata.netflix.com/Catalog/Genres()
然后我们可以用这个替换 LINQ 查询:
Uri uriQuery = new Uri("/Genres()", UriKind.Relative);
请注意,我们不需要指定 URI 的所有部分。这是因为 URI 的第一部分(http://odata.netflix.com/Catalog)已经被定义为 DataServiceContext
(参见本节开头),如您下面所见,我们在实例化 genres DataServiceCollection
时将其作为参数传递。
WCF Data Services 团队已将 LoadAsync(Uri)
方法添加到 DataServiceCollection
类中,这使我们的工作非常轻松,我们可以简单地将 LoadAsync
方法中的参数替换为 URI 查询。修改后的方法如下所示:
public void LoadGenres()
{
//Instantiate genres.
genres = new DataServiceCollection<genre>(context);
Uri uriQuery = new Uri("/Genres()", UriKind.Relative);
// Asynchronously load the result of the query.
genres.LoadAsync(uriQuery);
genres.LoadCompleted += (sender, args) =>
{
if (args.Error != null)
{
Debug.WriteLine("Requesting titles failed. " + args.Error.Message);
}
else
{
IsDataLoaded = true;
}
};
}
我们希望将查询结果绑定到 Panorama 控件。因此,在 MainViewModel
中,我们为 genres、newTitles 和 topTitles DataServiceCollection
s 声明了属性:
public DataServiceCollection<Genre> Genres
{
get
{
return genres;
}
private set
{
genres = value;
OnPropertyChanged("Genres");
}
}
public DataServiceCollection<Title> TopTitles
{
get
{
return topTitles;
}
private set
{
topTitles = value;
OnPropertyChanged("TopTitles");
}
}
public DataServiceCollection<Title> NewTitles
{
get
{
return newTitles;
}
private set
{
newTitles = value;
OnPropertyChanged("NewTitles");
}
}
在 MainPage
中,我们将 Panorama 控件中每个 ListBox
控件的 ItemSource
属性设置为绑定到上面显示的相应属性。此外,我们还将 TextBlock
控件的 Text
属性绑定到适当的字段名。下面是第一个 PanoramaItem
的示例,其中我们显示了所有电影类型的列表。
<!--Panorama item one-->
<controls:PanoramaItem Header="genres" >
<ListBox x:Name="GenreListBox" Margin="0,0,-12,0"
ItemsSource="{Binding Genres}"
SelectionChanged="GenreListBoxSelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding Name}" TextWrapping="Wrap"
Foreground="{StaticResource PanoramaForegroundBrush}"
Style="{StaticResource PhoneTextLargeStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PanoramaItem>
为了完成数据绑定,我们将 Panorama 页面(MainPage.cs)的 DataContext
设置为 MainViewModel
:
public MainPage()
{
InitializeComponent();
DataContext = new MainViewModel();
}
MainViewModel ViewModel
{
get
{
return (MainViewModel)DataContext;
}
}
WrapPanel
第三个 Panorama 项目显示了前 20 张 DVD 的缩略图。我决定利用 Silverlight for Windows Phone Toolkit,并选择使用 WrapPanel
来布局图像。顾名思义,WrapPanel
会在其边界内环绕内容。因此,您可以定义方向(默认为水平)、宽度和高度,内容就会很好地环绕在其中(参见图 2)。这与 StackPanel
相反,StackPanel
将内容水平或垂直堆叠,但不环绕内容。
要使用该工具包,我们需要添加对Microsoft.Phone.Controls.Toolkit.dll 程序集的引用,并在 MainPage.xaml 中添加命名空间引用。
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;
assembly=Microsoft.Phone.Controls.Toolkit"
因为此 Panorama 部分的内容比屏幕宽,所以我们将 PanoramaItem.Orientation
属性设置为 Horizontal
。正如您在下面的摘录中所见,我们使用一个 ListBox
控件和一个 ItemsPanelTemplate
,在其中指定用于布局要作为 WrapPanel
的项的 Panel
。我们将 WrapPanel
的宽度设置为 600px。方向默认为水平。然后,我们将 ListBox
DataTemplate
设置为图像,并将其源绑定到适当的字段。
<!--Panorama item tree-->
<controls:PanoramaItem Header="top rated"
Orientation="Horizontal">
<ListBox Margin="0,0,-12,0" x:Name="TopTitlesListBox"
ItemsSource="{Binding TopTitles}"
SelectionChanged="TitlesListBoxSelectionChanged">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel x:Name="wrapPanel" Width="600" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding BoxArt.MediumUrl}" Width="100" Margin="10"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</controls:PanoramaItem>
页面导航
Windows Phone Silverlight 应用程序中的页面导航基于 Silverlight 3 的 Frame 和 Page 导航模型。但是,Windows Phone 应用程序是使用 PhoneApplicationFrame
和 PhoneApplicationPage
构建的,而不是 Silverlight 的 3 Frame
和 Page
控件。这一点很重要,您不得使用标准的 Silverlight Frame
和 Page
类型。PhoneApplicationFrame
和 PhoneApplicationPage
控件分别继承自 Frame
和 Page
类。PhoneApplicationFrame
作为 PhoneApplicationPage
控件的容器,并包含系统托盘和应用程序栏等其他元素。PhoneApplicationPage
包含应用程序内容的各个部分。一个应用程序只能有一个 PhoneApplicationFrame
。其思想是,单个框架容器可以促进页面之间的导航(切换)。您可以创建任意数量的页面,但必须注意,每个页面都会保留在导航堆栈中,应用程序中的页面过多会增加导航堆栈的大小并可能降低性能。
页面之间的导航类似于使用 Web 浏览器浏览网页。向后导航是通过 Windows Phone 的*Back* 硬件按钮(将用户带到上一页或退出应用程序)或通过 NavigationService
进行的。
导航到 GenreDvds 页面
在第一个 Panorama 部分显示电影类型列表后,用户将被导航到 GenreDvds 页面并显示该电影类型的标题。然后,用户可以通过硬件返回按钮返回到上一页(参见图 3)。
当用户选择一个电影类型时,会触发 ListBox
的 SelectionChanged
事件。在处理程序中,我们检索选定的电影类型并将其保存为 Genre
对象。然后,我们使用 DataLayer
类来保存电影类型标题,并使用 NavigationService
的 Navigate
方法导航到 GenreDvds 页面。Navigate
方法接受一个 URI 作为参数,我们将其指定为相对于项目根目录的 GenreDvds 页面的路径,并传递一个 string
参数 genreId
。因为 gerneId
是一个电影类型名称,所以我们使用 HttpUtility.UrlEncode
方法将其编码为 URL 友好的字符串。在方法结束时,我们重置选定的项。
// Handle selection changed on ListBox
void GenreListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedGenre = GenreListBox.SelectedItem as Genre;
if (selectedGenre != null)
{
string genreId = HttpUtility.UrlEncode(selectedGenre.Name);
DataLayer.SetTitlesForGenre(genreId, selectedGenre.Titles);
NavigationService.Navigate(new Uri("/GenreDvds.xaml?name=" +
genreId, UriKind.Relative));
}
//Reset selected item.
GenreListBox.SelectedItem = null;
}
在 GenreDvds
页面上,我们在 OnNavigatedTo
方法中(当页面可见时调用)检索 string
参数,并调用 viewmodel
的 LoadGenreTitles
方法,将检索到的 genreId
传递给它。
// Set data context to selected item in the list.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string genreId;
if (!NavigationContext.QueryString.TryGetValue("name", out genreId))
{
MessageBox.Show("No genre provided.");
return;
}
viewModel.LoadGenreTitles(genreId);
}
GenreDvds
页面的 DataContext
在构造函数中设置为 GenreDvdsViewModel
。
readonly GenreDvdsViewModel viewModel = new GenreDvdsViewModel();
public GenreDvds()
{
InitializeComponent();
DataContext = viewModel;
}
在 LoadGenreTitles
方法中,我们使用 DataLayer
类检索所选电影类型的标题,并将 SelectedGenreName
属性设置为电影类型名称。我们还设置 Loaded
和 Busy
属性,用于设置 GenreDvds
页面上 ProgressBar
控件的可见性。这将在“进度条”部分进行解释。然后,我们异步加载相关的电影类型标题。genreTitles
字段是一个 DataServiceCollection
,它被声明为一个名为 GenreTitles
的属性,用于数据绑定。LoadGenreTitles
方法位于 GenreDvdsViewModel
中。
public void LoadGenreTitles(string genreName)
{
string genreId = HttpUtility.UrlEncode(genreName);
genreTitles = DataLayer.GetTitlesForGenre(genreId);
SelectedGenreName = genreName;
Loaded = false;
Busy = true;
if (genreTitles.Count() == 0)
{
// Load related titles.
genreTitles.LoadAsync();
}
else
{
Loaded = true;
Busy = false;
GetTitlesByYear(genreTitles);
GetTitlesByRating(genreTitles);
}
genreTitles.LoadCompleted += (sender, args) =>
{
if (args.Error != null)
{
Debug.WriteLine(
"Requesting titles failed. "
+ args.Error.Message);
}
Loaded = true;
Busy = false;
GetTitlesByYear(genreTitles);
GetTitlesByRating(genreTitles);
};
}
导航到详细信息页面
导航到详细信息页面始于用户在第二个或第三个 Panorama 部分中选择一个项目,并且处理方式与上面解释的相同。我们再次使用 DataLayer
类来设置选定的标题,然后导航到 DvdDetails
页面,并传递一个 string
参数。
// Handle selection changed on ListBox
void TitlesListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedDvd = ((ListBox)sender).SelectedItem as Title;
if (selectedDvd == null)
{
return;
}
string titleId = selectedDvd.Id;
DataLayer.SetSelectedTitle(titleId, selectedDvd);
// Navigate to the new page
NavigationService.Navigate(new Uri
("/DvdDetails.xaml?Id=" + titleId, UriKind.Relative));
// Reset selected item.
((ListBox)sender).SelectedItem = null;
}
区别在于,当我们导航到 DvdDetails
页面时,我们在 OnNavigatedTo
方法中直接将 DataContext
设置为选定的标题(从 DataLayer
类中检索)。
// Set data context to selected title.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string titleId;
if (NavigationContext.QueryString.TryGetValue("Id", out titleId))
{
DataContext = DataLayer.GetSelectedTitle(titleId);
}
}
保存和恢复瞬态状态
尽管这在演示应用程序中并未完全演示,但保存应用程序的瞬态状态是开发 Windows Phone 应用程序的重要组成部分。我通过阅读Daniel 关于 Windows Phone 应用程序生命周期的章节了解了这一点,并在此处对其进行总结。
无论何时中断应用程序,它都可能进入称为“墓碑化”的状态。墓碑化意味着应用程序已被终止,但以后可能会重新激活。当墓碑化时,您需要保存应用程序的当前状态,以便在应用程序重新激活时,用户会感觉好像什么都没有发生。
DataLayer
类(在演示中主要用于保存和检索对象)可以被应用程序用于跨页面保存和恢复其状态,以及在应用程序被停用时保存和恢复其状态。
public class DataLayer
{
static Dictionary<string, DataServiceCollection<Title>> titlesDictionary
= new Dictionary<string, DataServiceCollection<Title>>();
static Dictionary<string, Title> titleDictionary = new Dictionary<string, Title>();
const string titlesDictionaryKey = "DataLayer.titlesDictionary";
const string titleDictionaryKey = "DataLayer.titleDictionary";
public static void LoadFromDictionary(IDictionary<string, object> stateDictionary)
{
titlesDictionary = (Dictionary<string,
DataServiceCollection<Title>>)stateDictionary[titlesDictionaryKey];
titleDictionary = (Dictionary<string, Title>)stateDictionary[titleDictionaryKey];
}
public static void SaveToDictionary(IDictionary<string, object> stateDictionary)
{
stateDictionary[titlesDictionaryKey] = titlesDictionary;
stateDictionary[titleDictionaryKey] = titleDictionary;
}
public static void SetTitlesForGenre(string genreName,
DataServiceCollection<Title> titles)
{
titlesDictionary[genreName] = titles;
}
public static DataServiceCollection<Title> GetTitlesForGenre(string genreName)
{
return titlesDictionary[genreName];
}
public static void SetSelectedTitle(string id, Title title)
{
titleDictionary[id] = title;
}
public static Title GetSelectedTitle(string id)
{
return titleDictionary[id];
}
}
当应用程序被停用时,App
类会调用 DataLayer.SaveToDictionary
方法。这将 DataLayer
类的瞬态状态保存到状态 Dictionary
中,即使应用程序被墓碑化也能得以保留。
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
IDictionary<string> dictionary = PhoneApplicationService.Current.State;
DataLayer.SaveToDictionary(dictionary);
}
如果应用程序被重新激活,App
类会调用 DataLayer.LoadFromDictionary
方法,该方法会恢复 DataLayer
类的状态。
private void Application_Activated(object sender, ActivatedEventArgs e)
{
var dictionary = PhoneApplicationService.Current.State;
DataLayer.LoadFromDictionary(dictionary);
}
请注意,我在演示应用程序中没有保存 OData 调用结果,但这是应该做的事情。
Progress Bar
手机上支持的 ProgressBar
控件是 System.Windows.Controls
命名空间中的 Silverlight ProgressBar
。进度条用于指示耗时任务的进度。它可以是基于值的,也可以是无指示的。基于值的 ProgressBar
根据起始和结束值(默认为 0 和 100)指示任务进度,而无指示的 ProgressBar
显示一个重复动画,直到活动完成(参见图 4)。通过将无指示 ProgressBar
的 IsIndeterminate
属性设置为 true
来实现。在演示中,我们在 GenreDvds
上使用无指示 ProgressBar
来指示在加载电影类型标题时正在进行某些操作。为了演示目的,我使用了内置的 ProgressBar
控件,但如果您正在构建一个更重要的应用程序,需要无指示 ProgressBar
,您应该阅读 Jeff Wilcox 的帖子并使用他的 PerformanceProgressBar
。这是因为内置版本在性能方面成本很高。
提高 ProgressBar 无指示模式的效率
正如下面的摘录所示,ProgressBar
控件放置在一个 StackPanel
中,该 StackPanel
的 Visibility
属性与 viewmodel
的 Loaded
属性进行数据绑定。我使用了Daniel 的 IValueConverter
,称为 BooleanToVisibilityConverter
,将布尔值 Loaded
属性转换为 Visibility
类型。根据 Jeff Wilcox 的建议,ProgressBar
控件的 IsIndeterminate
属性不是直接设置为 true
,而是绑定到 viewmodel
的 Busy
属性,以确保重复动画能够正确关闭。ProgressBar
的 Visibility
属性再次绑定到 viewmodel
的 Loaded
属性,该属性通过我从 Daniel 那里借来的 BooleanToVisibilityConverter
转换为 Visibility
类型。
<StackPanel Grid.Row="1"
Visibility="{Binding Loaded,
Converter={StaticResource BooleanToVisibilityConverter},
ConverterParameter=Collapsed}"
Height="150" >
<TextBlock Text="Loading..." Style="{StaticResource PhoneTextTitle2Style}"
HorizontalAlignment="Center" Margin="20"/>
<ProgressBar IsIndeterminate="{Binding Busy}"
Visibility="{Binding Loaded,
Converter={StaticResource BooleanToVisibilityConverter},
ConverterParameter=Collapsed}" />
</StackPanel>
结论
在本文中,我们探讨了 Open Data 协议,并展示了如何在 Windows Phone 应用程序中消耗 OData。我们还探索了 Silverlight Toolkit 的 WrapPanel
控件,研究了页面导航,以及 ProgressBar
控件。我希望您发现本文有用。如果您喜欢我的文章,请给它评分,并在下方分享您的想法。
历史
- 2010 年 11 月:首次发布
参考文献
- Panorama 背景图片:Jana Koll 的红色背景