动手实验
实验 8: Windows应用商店API
2012年9月
简介
编写Windows应用商店应用最令人瞩目的理由之一是您可以方便地将它们发布到Windows应用商店。考虑到世界范围内目前有超过7亿台PC运行Windows 7,并且每台PC代表一个潜在的Windows 8升级,市场和收入潜力是巨大和多样的。鉴于收入分享计划将高达80%的销售收益分配给作者,开发者具有编写优秀应用并将它们提供给用户的充分动机。
应用商店具有灵活的盈利选项:您可以提供试用,一次性购买,应用内购买,第三方电子商务和广告。
对于试用,您可以使用位于Windows.ApplicationModel.Store命名空间的Windows应用商店API来检测应用程序是否运行于试用许可证。Windows运行时同时提供其他API以轻松地从试用版升级到付费版本,检索许可证信息并提供更多功能。Windows运行时中的CurrentAppSimulator类提供了方便的模拟购买和测试代码的方法,它们依赖于Windows应用商店API并且全部位于一个受控的环境中。
在本实验中您将使用Windows应用商店API来实现Contoso Cookbook的盈利。
首先,您将修改关于框以检测试用版,如果应用程序尚未付费则包含一个购买按钮。接着,当购买按钮被单击后您将使用CurrentAppSimulator模拟购买。最后,您将提供付费而不是免费的意大利食谱来模拟应用内购买。
目标
本实验将向您展示如何:
- 检测您的应用程序是否以试用版运行。
- 模拟在应用内部购买应用。
- 模拟应用内购买额外的产品。
- 检索有关应用程序和产品的许可证信息。
系统要求
您需要下列软件完成本实验:
- Microsoft Windows 8
- Microsoft Visual Studio 2012
设置
您必须执行以下步骤来准备本实验的计算机:
- 安装 Microsoft Windows 8。
- 安装 Microsoft Visual Studio 2012。
练习
本动手实验包含以下练习:
- 检测试用版
- 模拟应用购买
- 模拟产品购买
完成本实验的预计时间:30至40分钟。
练习 1:检测试用版
在本练习中您将使用Windows运行时中的Windows应用商店API来自定义Contoso Cookbook的开始页面内容。如果应用已经被购买,您将显示许可证信息。如果还未被购买(即以试用版运行),您将显示一个购买按钮。此外,在购买按钮上显示的价格并未被写死,而是来自从Windows应用商店检索的列表信息。
任务 1 – 添加许可证文件
我们将使用CurrentAppSimulator类来完成模拟购买,检索许可证信息以及其他工作。为了使模拟尽可能真实,我们将使用一个名称为license.xml的文件来向CurrentAppSimulator提供价格、过期日期等信息。
1、在Visual Studio中打开您在实验7中完成的ContosoCookbook项目。如果您尚未完成实验7或希望从一个参考副本开始,您可以在开始材料中找到实验已完成的版本。
2、如果项目中没有Data文件夹,则在解决方案资源管理器中创建该文件夹。
3、右键单击Data文件夹并使用Add > Existing Item命令从开始材料的data文件夹导入license.xml。
4、打开App.xaml.cs并向OnLaunched方法添加以下语句。将语句放在检查连接和订阅推送通知的if子句之后。
C#
// 初始化CurrentAppSimulator var file = await Package.Current.InstalledLocation.GetFileAsync("Data\\license.xml"); await Windows.ApplicationModel.Store.CurrentAppSimulator.ReloadSimulatorAsync(file);
5、打开license.xml并花一些时间检查其内容。<ListingInformation>元素包含有关应用程序自身和我们将在练习3中提供购买的意大利食谱产品的信息。<LicenseInformation>包含有关应用程序和产品的许可证信息。现实生活中所有这些信息将来自Windows应用商店。但是在模拟环境下,信息来自WindowsStoreProxy.xml。
任务 2 – 修改关于页面
现在让我们修改您在实验6中创建的关于页面。目前单词”试用版”出现在关于页面的应用程序标题下面。我们将使用Windows应用商店API来确定它是否的确是试用版并根据结果对页面进行自定义。
1、右键单击项目的DataModel文件夹并使用Add > New Item命令向项目添加一个新的类。将文件命名为AppLicenseDataSource.cs。
2、将文件内容替换如下。
C#
using Windows.ApplicationModel.Store; using Windows.Foundation; namespace ContosoCookbook { class AppLicenseDataSource : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private bool _licensed = false; private string _price; public AppLicenseDataSource() { if (CurrentAppSimulator.LicenseInformation.IsTrial) { CurrentAppSimulator.LicenseInformation.LicenseChanged += OnLicenseChanged; GetListingInformationAsync(); } else _licensed = true; } private async void GetListingInformationAsync() { var listing = await CurrentAppSimulator.LoadListingInformationAsync(); _price = listing.FormattedPrice; } private void OnLicenseChanged() { if (!CurrentAppSimulator.LicenseInformation.IsTrial) { _licensed = true; CurrentAppSimulator.LicenseInformation.LicenseChanged -= OnLicenseChanged; ((ContosoCookbook.App)App.Current).Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("IsLicensed")); PropertyChanged(this, new PropertyChangedEventArgs("IsTrial")); PropertyChanged(this, new PropertyChangedEventArgs("LicenseInfo")); } }); } } public bool IsLicensed { get { return _licensed; } } public bool IsTrial { get { return !_licensed; } } public string LicenseInfo { get { if (!_licensed) return "Trial Version"; else return ("Valid until " + CurrentAppSimulator.LicenseInformation.ExpirationDate.LocalDateTime.ToString("dddd, MMMM d, yyyy")); } } public string FormattedPrice { get { if (!String.IsNullOrEmpty(_price)) return "Upgrade to the Full Version for " + _price; else return "Upgrade to the Full Version"; } } } } }
注意:您刚添加的AppLicenseDataSource类实现了INotifyPropertyChanged接口并公开了名称为IsLicensed, IsTrial, LicenseInfo和 FormattedPrice的属性。前面两个使用CurrentAppSimulator.LicenseInformation.IsTrial来确定现在运行的应用程序是否已经购买或以试用版运行。如果应用程序是试用版,LicenseInfo返回“Trial Version”字符串,或者如果不是试用版,返回包含许可证的过期日期的字符串。FormattedPrice返回一个包含应用程序价格的按钮标签,该价格从Windows应用商店获得(或者在这里来自WindowsStoreProxy.xml)。价格信息来自通过CurrentAppSimulator.LoadListingInformationAsync 检索获得的ListingInformation对象。
3、打开AboutUserControl.xaml并向页面顶部的UserControl元素添加xmlns:common="using:ContosoCookbook.Common"属性。
XAML
<UserControl x:Class="ContosoCookbook.AboutUserControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:ContosoCookbook" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:common="using:ContosoCookbook.Common" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
4、现在在Grid元素上方添加以下语句。
XAML
<UserControl.Resources> <local:AppLicenseDataSource x:Key="License" /> <common:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> </UserControl.Resources>
注意:BooleanToVisibilityConverter是一个简单的值转换器,它将布尔值true转换为Visibility.Visible,将false转换为Visibility.Collapsed。它在Visual Studio创建项目时已经被包含在其中。
5、将text属性为“Trial Version”的TextBlock替换为以下语句。
XAML
<TextBlock Text="{Binding LicenseInfo, Source={StaticResource License}}" FontFamily="Segoe UI" FontWeight="SemiLight" FontSize="18" TextWrapping="Wrap" /> <Button x:Name="PurchaseButton" Width="225" Height="120" Margin="0,24,0,0" Visibility="{Binding IsTrial, Source={StaticResource License}, Converter={StaticResource BooleanToVisibilityConverter }}"> <Button.Content> <TextBlock Text="{Binding FormattedPrice, Source={StaticResource License}}" TextWrapping="Wrap" TextAlignment="Center" /> </Button.Content> </Button>
</Button>
6、花一些时间检查您刚添加的XAML。TextBlock的文本现在来自AppLicenseDataSource对象的LicenseInfo属性。按钮的文本来自相同对象的FormattedPrice属性,并且按钮是否可见被链接到IsTrial属性。结果是按钮甚至不会在页面中显示,除非应用程序以试用版运行。
任务 3 – 测试结果
现在让我们测试这些修改以查看CurrentAppSimulator和WindowsStoreProxy.xml的运行。
1、按F5启动应用程序。
2、显示超级按钮并点击设置超级按钮。
3、在设置菜单点击About以显示关于页面。
4、确认购买按钮显示在关于页面上,并且价格是$12.99,如图1所示。
图 1 应用程序试用版的关于页面
5、返回Visual Studio并停止调试。
6、打开license.xml并在<App>部分的<Price>元素中将购买价格由$12.99修改为$8.99。
7、再次启动应用程序并转到关于页面。现在购买按钮上显示的价格是多少?
8、再次返回Visual Studio并停止调试。
9、再次打开license.xml并将价格修改回$12.99。同样将<IsTrial>从“true”修改为“false”。
10、启动应用程序并转到关于页面。验证购买按钮已经消失并且您现在将看到消息“Valid until Saturday, December 31, 2022”(在2022年12月31日周六前一直有效),如图2所示。
图 2 应用程序已购买版本的关于页面
11、返回Visual Studio并停止调试。
12、为准备下一个练习,在license.xml中将<IsTrial>从“false”改回到“true”。
练习 2: 模拟应用购买
您可以使用license.xml来测试基于应用程序是否为试用版的用户界面的改变,但是它并不能代替对实际购买的模拟。在本练习中您将为购买按钮编写处理程序,这样您就可以从Windows应用商店“购买”应用程序了。
任务 1 – 为购买按钮编写处理程序
为模拟应用程序购买,我们将在用户单击关于页面的购买按钮时调用CurrentAppSimulator.RequestAppPurchaseAsync。并且为了对购买成功完成进行检测,我们将使用已经存在于AppLicenseDataSource中的LicenseChanged事件处理程序。
1、打开AboutUserControl.xaml。
2、向购买按钮添加以下Click属性。
XAML
Click="OnPurchaseButtonClicked"
3、打开AboutUserControl.xaml.cs并添加以下using语句。
C#
using Windows.ApplicationModel.Store;
4、然后添加以下事件处理程序。
C#
private void OnPurchaseButtonClicked(object sender, RoutedEventArgs e) { // 购买应用程序 CurrentAppSimulator.RequestAppPurchaseAsync(false); }
注意:RequestAppPurchaseAsync是一个异步方法。为了确定购买是否执行,您将处理LicenseChanged事件。您没有在这里处理该事件是因为您已经在AppLicenseDataSource.cs中进行了处理。当LicenseChanged事件被触发时,处理程序验证应用程序已经不再是试用版并触发PropertyChanged事件来更新绑定到IsTrial和其他AppLicenseDataSource属性的控件。
5、在测试许可证之前,我们需要缓存App对象的调度程序(dispatcher),因为LicenseChanged事件可以在后台线程中触发,并且我们在事件中使用数据绑定来更新用户界面。打开App.xaml.cs并向App类添加以下成员和属性。
C#
Windows.UI.Core.CoreDispatcher _dispatcher = null; public Windows.UI.Core.CoreDispatcher Dispatcher { get { return _dispatcher; } }
6、在App类重写OnWindowCreated方法,这样我们就可以捕获到窗口的dispatcher。
C#
protected override void OnWindowCreated(WindowCreatedEventArgs args) { _dispatcher = args.Window.Dispatcher; base.OnWindowCreated(args); }
任务 2 – 购买应用
现在让我们模拟应用程序购买。请注意CurrentAppSimulator将购买和许可证状态的更改信息存储在内存中,它并未将它们记录在WindowsStoreProxy.xml中。当您购买应用程序后,只要应用程序在运行,它将保持“已购买”状态;但是当您重新启动应用程序时,您将再次运行试用版。
1、按F5以启动应用程序。
2、转到关于页面并点击购买按钮以模拟应用程序购买。
3、在Windows应用商店对话框中点击Continue(继续)按钮完成模拟购买。
4、再次显示关于页面并确认购买按钮已经消失。
注意:在被购买后Contoso Cookbook并未向用户公开额外的功能,它仅用许可证信息替换购买按钮。在现实生活中,您可以选择限制试用版用户的功能并在购买后公开所有的功能。
5、返回Visual Studio并停止调试。
练习 3: 模拟产品购买
除了允许应用程序购买以外,Windows应用商店还支持应用内的产品购买。例如某个游戏在用户完成上一个级别后可以允许用户购买游戏的其他级别。在Windows应用商店术语中,以这种方式购买的功能称为产品,并且Windows运行时为产品购买、确定哪些产品已经被购买、这些产品的许可证状态等内容提供了您所需要的API。
在本练习中您将修改Contoso Cookbook以使意大利食谱不再免费,即查看前必须购买。您将添加一个简单的购买它们的用户界面,这个用户界面基于CurrentAppSimulator,同时您还将添加在产品购买之后才能显示所有意大利食谱的逻辑。
任务 1 – 修改项-明细页面
第一步是添加一个数据源类以提供产品许可证信息。然后我们将添加一个购买按钮并使用数据绑定来确保对于意大利食谱,购买按钮或食谱指南只有其中之一被显示,而不是两者都被显示。
1、右键单击项目的DataModel文件夹并使用Add > New Item命令向项目添加一个新的类。将文件命名为ProductLicenseDataSource.cs。
2、将文件内容替换如下。
C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.ApplicationModel.Store; namespace ContosoCookbook { class ProductLicenseDataSource : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private const string _name = "ItalianRecipes"; private bool _licensed = false; private string _price; public string GroupTitle { set { if (value != "Italian") _licensed = true; else if (CurrentAppSimulator.LicenseInformation.ProductLicenses[_name].IsActive) _licensed = true; else { CurrentAppSimulator.LicenseInformation.LicenseChanged += OnLicenseChanged; GetListingInformationAsync(); } } } private async void GetListingInformationAsync() { var listing = await CurrentAppSimulator.LoadListingInformationAsync(); _price = listing.ProductListings[_name].FormattedPrice; } private void OnLicenseChanged() { if (CurrentAppSimulator.LicenseInformation.ProductLicenses[_name].IsActive) { _licensed = true; CurrentAppSimulator.LicenseInformation.LicenseChanged -= OnLicenseChanged; ((ContosoCookbook.App)App.Current).Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("IsLicensed")); PropertyChanged(this, new PropertyChangedEventArgs("IsTrial")); } }); } } public bool IsLicensed { get { return _licensed; } } public bool IsTrial { get { return !_licensed; } } public string FormattedPrice { get { if (!String.IsNullOrEmpty(_price)) return "Purchase Italian Recipes for " + _price; else return "Purchase Italian Recipes"; } } } }
注意:ProductLicenseDataSource类似于您之前添加的AppLicenseDataSource类。ProductLicenseDataSource对名称为“ItalianRecipes”的产品状态信息进行封装,而AppLicenseDataSource对应用程序的许可证状态数据进行封装。该产品在license.xml中定义。
3、打开ItemDetailPage.xaml并向<Page.Resources>部分添加以下语句。
XAML
<local:ProductLicenseDataSource x:Key="License" /> <common:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
4、找到两个Text属性是“{Binding Directions}”的TextBlock元素。将每个TextBlock替换为以下语句。
XAML
<TextBlock FontSize="20" FontWeight="Light" Text="{Binding Directions}" TextWrapping="Wrap" Visibility="{Binding IsLicensed, Source={StaticResource License}, Converter={StaticResource BooleanToVisibilityConverter }}" /> <Button Width="225" Height="120" Background="#30ffffff" Click="OnPurchaseProduct" Visibility="{Binding IsTrial, Source={StaticResource License}, Converter={StaticResource BooleanToVisibilityConverter }}"> <Button.Content> <TextBlock Text="{Binding FormattedPrice, Source={StaticResource License}}" TextWrapping="Wrap" TextAlignment="Center" /> </Button.Content> </Button>
5、打开ItemDetailPage.xaml.cs并在文件顶部添加以下using语句。
C#
using Windows.ApplicationModel.Store;
6、在LoadState方法的结束处添加以下语句。
C#
// 将组标题传递给LicenseDataSource (重要!) ProductLicenseDataSource license = (ProductLicenseDataSource)this.Resources["License"]; license.GroupTitle = item.Group.Title;
注意:上述代码很重要,因为它使得ProductLicenseDataSource知道当前显示的食谱是否是意大利食谱或是其他食谱。
7、向ItemDetailPage.xaml.cs添加以下方法以在购买按钮被单击时请求产品购买。
C#
private void OnPurchaseProduct(object sender, RoutedEventArgs e) { // 首先检查是否是试用版,您不能在试用版购买产品 if (CurrentAppSimulator.LicenseInformation.IsTrial) new Windows.UI.Popups.MessageDialog("Please go into About page in Settings and license first", "You must upgrade from trial first").ShowAsync(); else { // 购买意大利食谱产品 CurrentAppSimulator.RequestProductPurchaseAsync("ItalianRecipes", false); } }
任务 2 – 进行产品购买
现在所有剩下的工作就是测试您的修改并观察产品购买的运行。
1、按F5启动应用程序。
2、点击某个意大利食谱以转到项-明细页面。
3、确认购买按钮出现在烹饪指南处,如图3所示。
图 3 产品购买用户界面
4、点击按钮以启动产品购买。
5、在Windows应用商店对话框中点击Continue以模拟产品购买。
6、确认按钮消失并且烹饪指南出现在它的位置。
7、查看一些其他的意大利食谱并验证烹饪指南按预期方式显示。
8、返回Visual Studio并停止调试。
总结
您在本实验中进行的练习演示了一些Windows应用商店API最重要的方面:如何检测试用版,如何模拟应用程序购买,如何模拟应用内的产品购买以及如何检索有关这些产品的信息。当然在实际的应用程序中,您将以CurrentApp调用替换CurrentAppSimulator调用。这样您将具备通过应用程序盈利的所有工具。请继续前行并创造收益!
祝您在Windows 8中编程快乐!