动手实验
2012年9月
Contoso Cookbook是一系列的动手实验教程,这些实验被设计用来使您沉浸于Windows应用商店应用的开发。当您完成本实验后,您将创建一个漂亮并实用的真实应用程序。该应用程序将使用Window8中的一些关键的新特性。通过本系列实验,您将了解很多创建优秀Windows应用商店应用的知识,包括:
在本系列的第一个实验中,您将使用可扩展应用程序标记语言(XAML)和C#创建应用,实现导航,从Windows Azure下载数据(如果没有互联网连接可从本地加载),以及使用数据绑定将数据连接到控件。
本实验将向您展示如何:
您需要下列软件完成本实验:
您必须执行以下步骤来准备本实验室的计算机:
本实验包含下列练习:
完成本实验的预计时间:30至45分钟。
在第一个练习中,您将在Visual Studio中创建一个新的包含C#网格应用项目的解决方案。然后您将检查Visual Studio生成的文件并进行一些简单的修改以实现用户界面的自定义。
任务1 –创建项目
第一步是创建一个新的项目,利用包含的代码和资源构成Contoso Cookbook应用,并观察Visual Studio项目中的内容。
1、启动Visual Studio并使用File > New Project命令创建一个新的名称为“ContosoCookbook”的Visual C#项目。从Visual C#模板列表中选择Windows Store,并从模板类别列表中选择Grid App (XAML),如图1所示。
图1 创建ContosoCookbook项目
2、从Debug菜单中选择Start Debugging (或按F5)在调试器中运行应用。您将看到图2显示的屏幕。这是应用的主页或开始页面。
图2 Contoso Cookbook的开始页面
3、花些时间运行应用程序。对初学者来说,使用鼠标(如果您的电脑支持触摸屏,可使用手指)水平滚动屏幕。
注意:网格布局和水平滚动由GridView控件提供,该控件是Windows 运行时中Windows.UI.Xaml.Controls命名空间提供的许多用于创建丰富并引人注目的用户界面的控件之一。
4、触摸或单击一个GridView项,看看会发生什么。例如,点击标记为Item Title: 1的项目以显示图3所示的屏幕。这是项-明细页面(item-detail page)。
注意:Windows 8被描述为“触摸优先”的操作系统,但同时对鼠标和手写笔等传统输入设备提供良好的支持。从现在开始,当被告知“触摸”或“点击”屏幕中的某个内容时,请注意您并不需要触摸屏才能完成。单击一下鼠标即可!
图3 项-明细页面
注意:当位于项-明细页面时,您可以水平滚动以查看本组中的所有项目。(如果使用鼠标,单击屏幕左侧和右侧边缘的箭头。)上述滚动由FlipView控件提供,该控件是Windows.UI.Xaml.Controls命名空间中的另一个控件。
5、通过点击屏幕左上角的返回按钮(带圆圈的箭头)返回开始页面。
6、点击开始页面左上角的ContosoCookbook下的Group Title: 1以显示组-明细页面(group-detail page,图4)。
图4 组-明细页面
7、切换回Visual Studio。(如果您正在使用触摸屏,最简单的方法是从屏幕左侧边缘开始从左向右轻扫;如果您喜欢使用键盘,按Windows徽标键+D。)然后从Debug菜单中选择Stop Debugging以停止应用程序。
任务2 –熟悉项目
很明显当Visual Studio生成项目时免费提供了很多东西。具体地说,它为您提供了一些XAML页面,逻辑以及在页面间导航的用户界面(包括可以工作的后退按钮),以及示例数据资源。为实现Contoso Cookbook,我们将在Visual Studio生成的基础上进行构建。但是首先花些时间熟悉项目的结构和Visual Studio创建的资产。
1、在解决方案窗口, 查看项目根文件夹的内容。您将在此处找到四个关键的文件,加上与它们相关的代码隐藏文件:
◦ App.xaml代表本应用和应用的资源
◦ GroupedItemsPage.xaml代表本应用的开始页面
◦ ItemDetailPage.xaml代表项-明细页面
◦ GroupDetailPage.xaml代表组-明细页面
2、查看项目的Assets文件夹,您会发现用于应用程序品牌推广的图像资产。
3、查看项目的Common文件夹。您会发现BooleanToVisibilityConverter.cs,它包含了将布尔值 true和false转换为Visibility.Visible和Visibility.Collapsed属性的值转换器,以及名称为StandardStyles.xaml的文件,它包含了用于设置应用样式的XAML资源。
4、查看项目的DataModel文件夹,您会发现名称为SampleDataSource.cs的文件,它包含了描述数据的类以及与其相关的示例数据。
任务3 – 自定义开始页面
目前项目名称以” ContosoCookbook”出现在开始页面的顶部。让我们把它修改为” Contoso Cookbook”。
1、在Visual Studio中打开App.xaml。
2、找到名称为AppName的字符串资源并将它的值由“ContosoCookbook” 修改为“Contoso Cookbook”,如这里所示。
XAML
<x:String x:Key="AppName">Contoso Cookbook</x:String>
3、按F5在调试器中启动应用程序并确认开始页面顶部的标题文本已修改(图5)。
图5修改后的开始页面
4、返回Visual Studio并使用Stop Debugging命令关闭应用程序。
任务4 – 自定义品牌
如果现在进入Windows 8的开始屏幕,您会发现名为“ContosoCookbook”的磁贴。这个磁贴是应用的主磁贴。它在应用程序安装时被创建,安装则发生在应用程序从Visual Studio第一次启动时。磁贴上的图像来自Assets文件夹中的Logo.png。在本任务中,您将用更适合食谱应用的徽标替换Visual Studio生成的徽标。同时您将替换Assets文件夹中的其它PNG文件以唯一地进行应用程序的品牌推广,并通过修改应用程序清单最终完成工作。
1、在Windows 8开始屏幕,右键单击ContosoCookbook磁贴(或在放手之前用手指向下拖动磁贴半英寸左右)并选择卸载以卸载应用并移除磁贴。
2、返回Visual Studio并右键单击Assets文件夹。然后使用Add > Existing Item命令从实验开始材料中的Images文件夹导入Logo.png, SmallLogo.png,SplashScreen.png, StoreLogo.png和WideLogo.png。当收到提示时,允许Visual Studio覆盖现有的同名文件。
3、在解决方案管理器中双击Package.appxmanifest并打开应用程序清单。
注意:应用程序清单包含应用程序的元数据并被嵌入到生成的每个应用程序中。在运行时应用程序清单告诉Windows 8有关应用程序的一切,包括应用程序的名称,发布者和应用程序需要的功能。这些功能包括访问网络摄像机,麦克风,Internet以及一部分文件系统,具体指用户的文档库,音乐库以及视频库。
4、将应用程序的显示名称修改为“Contoso Cookbook”并将说明修改为“Contoso Cookbook Application”,如图6所示。同时在宽徽标框中输入“Assets\WideLogo.png”,以给应用增加一个宽徽标。
图6 修改清单中的品牌
5、按F5启动应用程序。
6、观察应用程序的启动。初始屏幕(当应用程序加载时简单地被显示的屏幕)是否和以前有所不同?
7、转到Windows 8开始屏幕并确认包含如下磁贴。
图7 新的应用程序磁贴
注意:如果您喜欢方形磁贴,右键单击宽磁贴(或者在触摸屏上,轻轻向下拖动磁贴并放手)然后在应用栏中单击缩小按钮。
8、返回Visual Studio并停止调试。
项目中已经包含了示例数据,但是您需要用您的数据将其替换。在练习2中,您将用真实的食谱数据和图像替换示例数据,
任务1 – 导入食谱数据类
第一步是用食谱数据类替换Visual Studio提供的示例数据类。
1、在解决方案管理器中右键单击DataModel文件夹并使用Add > Existing Item命令导入开始材料Data文件夹中的RecipeDataSource.cs。
注意:Visual Studio提供了一个名为SampleDataSource.cs的文件,它包含名称为SampleDataCommon, SampleDataItem, SampleDataGroup和 SampleDataSource的数据类。RecipeDataSource.cs包含了适用于食谱数据的相同的类的版本:RecipeDataCommon, RecipeDataItem, RecipeDataGroup和 RecipeDataSource。不同于SampleDataSource.cs中包含的硬编码示例数据,RecipeDataSource.cs包含名称为LoadLocalDataAsync和LoadRemoteDataAsync的方法,上述方法从您将在下一任务中导入的文件或Windows Azure中加载食谱数据。它同时包含用于解释JSON格式食谱数据以及将数据加载到RecipeDataItem 和RecipeDataGroup实例的所有Windows.Data.Json代码。仔细查看代码以理解如何加载和使用数据。特别是LoadRemoteDataAsync方法,它使用.NET框架中的HttpClient类从云中加载食谱数据。
2、打开GroupedItemsPage.xaml.cs并将所有对SampleDataSource的引用修改为RecipeDataSource,所有对SampleDataGroup的引用修改为RecipeDataGroup,所有对SampleDataItem的引用修改为RecipeDataItem。
3、对GroupDetailPage.xaml.cs执行相同的操作。
4、对ItemDetailPage.xaml.cs执行相同的操作。
注意:一个安全并简单的进行上述‘替换’的方法是在解决方案管理器中右键单击SampleDataSource.cs并选择Exclude From Project(从项目中排除)。当把示例数据排除后,应用程序将不能编译,此时您将确切知道应该在何处进行修改。
任务2 – 加载食谱数据
下一任务是导入食谱图像并修改应用程序以加载食谱数据。
1、在ContosoCookbook项目中创建名称为“Data”的新文件夹。您可以右键单击解决方案中的项目并选择Add > New Folder。
2、右键单击Data文件夹并使用Add > Existing Item命令从开始材料的Data文件夹导入Recipes.txt。
注意:查看一下Recipes.txt,您会发现它包含JSON编码的表示食谱和食谱组的数据。
3、在项目中添加一个名为“Images”的文件夹。
4、从开始材料的Images文件夹导入名称为tiles, chinese, french, german, indian, italian和 mexican的文件夹(以及文件夹的内容)。将这些文件夹放在Images文件夹中非常重要,因为Recipes.txt中的统一资源定位器(URLs)假定它们位于Images文件夹。
注意:一个导入文件夹的简单方法是从Windows 8文件管理器拖动文件夹并在解决方案管理器的Images文件夹上释放。
5、打开App.xaml.cs并在顶部添加以下using语句。
C#
using ContosoCookbook.Data;
6、在OnLaunched事件处理程序中,添加async调用以使用RecipeDataSource.LoadLocalDataAsync方法加载食谱。在检查PreviousExecutionState 的if语句之后执行上述操作。
C#
if (args.PreviousExecutionState == ApplicationExecutionState.Running) { Window.Current.Activate(); return; } // Load recipe data await RecipeDataSource.LoadLocalDataAsync(); // Place the frame in the current Window Window.Current.Content = rootFrame;
注意:RecipeDataSource.LoadLocalDataAsync方法从您已导入的Recipes.txt文件中读取JSON格式的食谱数据。Recipes.txt中的图像统一资源定位器(URLs)指向项目中Images文件夹中的图像。如果您愿意,您可以通过将调用由RecipeDataSource.LoadLocalDataAsync替换为 RecipeDataSource.LoadRemoteDataAsync以便从Windows Azure下载食谱数据。这样食谱数据将来自云而非来自本地资源。如果决定这么做,您可以从项目中移除Recipes.txt。然而Images文件夹必须保留,因为它包含实验6中用于创建辅助磁贴的150 x 150像素的食谱图像。辅助磁贴图像必须是本地资源,它们不能远程加载。
任务3 – 测试结果
现在运行应用程序并观察Contoso Cookbook的变化。
1、按F5调试应用程序并验证开始页面如下所示。
图8显示食谱的开始页面
2、返回Visual Studio并停止调试。
考虑到目前为止我们只编写了很少的代码,这是一个很好的开始。但是我们需要自定义用户界面并针对特定数据模型对其进行改造。在本练习中,您将修改开始页面,项-明细页面和组-明细页面以对Contoso Cookbook的外观进行完善。
任务1 – 修改开始页面
让我们修改开始页面以改善食谱项的外观。
1、打开位于项目Common文件夹中的StandardStyles.xaml。
2、找到名称为“Standard250x250ItemTemplate”的DataTemplate元素。这是用于在开始页面呈现食谱项的数据模板。
3、在数据模板中移除最后一个TextBlock 元素(Text 属性绑定到“Subtitle” 的TextBlock),因为RecipeDataItem类没有Subtitle属性。
4、在上述数据模板中,将 Grid元素的宽度和高度修改为320×240以保持原始食谱图像的高宽比。此外将剩下的TextBlock的高度由60修改为48以降低每一项部分透明的重叠区域的高度,同时将绑定到TextBlock的Text属性的属性由Title修改为ShortTitle。当您完成以上工作,您的代码将是这样。
XAML
<DataTemplate x:Key="Standard250x250ItemTemplate"> <Grid HorizontalAlignment="Left" Width="320" Height="240"> <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}"> <Image Source="{Binding Image}" Stretch="UniformToFill"/> </Border> <StackPanel VerticalAlignment="Bottom" Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}"> <TextBlock Text="{Binding ShortTitle}" Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextStyle}" Height="48" Margin="15,0,15,0"/> </StackPanel> </Grid> </DataTemplate>
5、现在按F5运行应用程序。确认开始页面的食谱项与此处显示一致。
图9 新的改进后的开始页面
6、返回Visual Studio并停止调试。
任务2 – 修改组-明细页面
您已经修改了开始页面以改进应用程序的外观,但是您还需要修改组-明细页面。在本任务中,您将修改此页面并使它更漂亮。
1、再次启动应用程序并点击屏幕左上角的Chinese进入显示中餐食谱的组-明细页面。我们进行的修改不多:缩小“Chinese”和下面图像之间的空间,将食谱标题替换为短标题并为每个食谱添加准备时间。
2、返回Visual Studio并停止调试。
3、打开GroupDetailPage.xaml并找到GridView.Header元素。删除第一个TextBlock。在下一行的图像元素,将‘Height=”400”’替换为‘Width=”480”’并将顶部边距由0修改为10。代码现在应该是这样。
XAML
<GridView.Header> <StackPanel Width="480" Margin="0,4,14,0"> <Image Source="{Binding Image}" Width="480" Margin="0,10,18,20" Stretch="UniformToFill"/> <TextBlock Text="{Binding Description}" Margin="0,0,18,0" Style="{StaticResource BodyTextStyle}"/> </StackPanel> </GridView.Header>
4、 现在返回StandardStyles.xaml并找到名称为“Standard500x130ItemTemplate”的DataTemplate元素。这是在组-明细页面呈现食谱项的数据模板。
5、在数据模板中将Grid的宽度由480修改为360。
6、在数据模板中从Border中删除‘Width=”110”’属性以保持食谱图像的高宽比。保留‘Height=”110”’属性。
7、删除两个TextBlock元素,它们的Text属性绑定到数据源的Subtitle和Description属性。
8、在剩下的TextBlock中,将绑定到Text属性的属性由Title修改为ShortTitle。
9、在TextBlock下面,添加下列语句以在食谱标题下包含准备时间。
XAML
<StackPanel Orientation="Horizontal"> <TextBlock Text="Preparation time:" Style="{StaticResource BodyTextStyle}" /> <TextBlock Text="{Binding PrepTime}" Style="{StaticResource BodyTextStyle}" Margin="4,0,4,0" /> <TextBlock Text="minutes" Style="{StaticResource BodyTextStyle}" /> </StackPanel>
10、完成以上工作,修改后的数据模板应该是这样。
XAML
<DataTemplate x:Key="Standard500x130ItemTemplate"> <Grid Height="110" Width="360" Margin="10"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Height="110"> <Image Source="{Binding Image}" Stretch="UniformToFill"/> </Border> <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0"> <TextBlock Text="{Binding ShortTitle}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/> <StackPanel Orientation="Horizontal"> <TextBlock Text="Preparation time:" Style="{StaticResource BodyTextStyle}" /> <TextBlock Text="{Binding PrepTime}" Style="{StaticResource BodyTextStyle}" Margin="4,0,4,0" /> <TextBlock Text="minutes" Style="{StaticResource BodyTextStyle}" /> </StackPanel> </StackPanel> </Grid> </DataTemplate>
11、应用程序并点击任一组标题。验证组-明细页面如下所示。
图10 修改后的组-明细页面
12、返回Visual Studio并停止调试。
任务 3 – 修改项-明细页面
为应用程序制作基本用户界面的最后任务是修改项-明细页面以呈现有关食谱的更多信息。
1、运行应用程序并点击Fried Dumpling。显然需要对项-明细页面进行一些修改。
2、返回Visual Studio并停止调试。
3、在解决方案管理器中右键单击Common文件夹并使用Add > New Item命令在项目中添加一个新的类。将文件命名为ListConverter.cs。
4、用以下代码替换文件的内容。
C#
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.UI.Xaml.Data; namespace ContosoCookbook.Common { class ListConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { ObservableCollection<string> items = (ObservableCollection<string>) value; StringBuilder builder = new StringBuilder(); foreach(var item in items) { builder.Append(item); builder.Append("\r\n"); } return builder.ToString(); } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } }
注意:ListConverter是一个值转换器,它将将一组字符串转换为包含换行符的一个单独的字符串。我们需要这个值转换器,因为我们将TextBlock的Text属性绑定到一组字符串,它需要一个值转换器。
5、打开ItemDetailPage.xaml并在靠近文件顶部的<Page.Resources>部分添加以下语句以声明ListConverter实例。
XAML
<common:ListConverter x:Key="ListConverter" />
6、滚动ItemDetailPage.xaml并找到名称为“flipView”的FlipView元素。
7、用以下内容替换FlipView.ItemDataTemplate元素内的DataTemplate元素。
XAML
<DataTemplate> <UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates"> <ScrollViewer x:Name="scrollViewer" Style="{StaticResource VerticalScrollViewerStyle}" Grid.Row="1"> <!-- Three-column grid for item-detail layout --> <Grid Margin="120,0,20,20"> <Grid.ColumnDefinitions> <ColumnDefinition Width="400" /> <ColumnDefinition Width="40" /> <ColumnDefinition Width="360" /> <ColumnDefinition Width="40" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <StackPanel Orientation="Vertical" Grid.Column="0"> <TextBlock FontSize="26.667" FontWeight="Light" Text="{Binding Title}" TextWrapping="Wrap"/> <Image x:Name="image" Width="400" Margin="0,20,0,10" Stretch="Uniform" Source="{Binding Image}"/> <StackPanel Orientation="Horizontal"> <TextBlock FontSize="26.667" FontWeight="Light" Text="Preparation time:"/> <TextBlock FontSize="26.667" FontWeight="Light" Text="{Binding PrepTime}" Margin="8,0,8,0"/> <TextBlock FontSize="26.667" FontWeight="Light" Text="minutes"/> </StackPanel> </StackPanel> <StackPanel Orientation="Vertical" Grid.Column="2"> <TextBlock FontSize="26.667" FontWeight="Light" Text="Ingredients" Margin="0,0,0,16"/> <TextBlock FontSize="20" FontWeight="Light" LineHeight="32.5" Text="{Binding Ingredients, Converter={StaticResource ListConverter}}" TextWrapping="Wrap" /> </StackPanel> <StackPanel Orientation="Vertical" Grid.Column="4"> <TextBlock FontSize="26.667" FontWeight="Light" Text="Directions" Margin="0,0,0,16"/> <ScrollViewer Style="{StaticResource VerticalScrollViewerStyle}"> <Grid> <TextBlock FontSize="20" FontWeight="Light" Text="{Binding Directions}" TextWrapping="Wrap" /> </Grid> </ScrollViewer> </StackPanel> </Grid> <VisualStateManager.VisualStateGroups> <!-- Visual states reflect the app's view state inside the FlipView --> <VisualStateGroup x:Name="ApplicationViewStates"> <VisualState x:Name="FullScreenLandscape"/> <VisualState x:Name="Filled"/> <!-- Respect the narrower 100-pixel margin convention for portrait --> <VisualState x:Name="FullScreenPortrait"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="image" Storyboard.TargetProperty="MaxHeight"> <DiscreteObjectKeyFrame KeyTime="0" Value="400"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <!-- When snapped, the content is reformatted and scrolls vertically --> <VisualState x:Name="Snapped"> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="scrollViewer" Storyboard.TargetProperty="Style"> <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource VerticalScrollViewerStyle}"/> </ObjectAnimationUsingKeyFrames> <ObjectAnimationUsingKeyFrames Storyboard.TargetName="image" Storyboard.TargetProperty="MaxHeight"> <DiscreteObjectKeyFrame KeyTime="0" Value="160"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> </ScrollViewer> </UserControl> </DataTemplate>
注意:新的数据模板以3列格式显示食谱。食谱名称,图像和准备时间在第1列,原料列表在第2列,烹饪指南在第3列。
8、现在再次运行应用程序。点击Fried Dumpling并验证项-明细页面应该如图11所示。
图11 修改后的项-明细页面
9、返回Visual Studio并停止调试。
在本实验中,您使用Visual Studio中的网格应用程序项目创建了一个新的Windows应用商店应用。然后您用真实数据替换了示例数据,用定制的应用程序品牌资产替换了缺省的品牌资产,并通过修改一些Visual Studio提供的XAML对用户界面进行了定制。此外您亲自动手了解了项目的结构以及各组成部分如何相互配合。
您还导入了一些代码,这些代码演示如何利用System.Net.Http.HttpClient类从远程数据源加载数据以及如何利用Windows运行时中的Windows.Data.Json类在C#中使用JSON数据。通过修改数据模板,您对数据呈现给用户的方式进行了定制。
这是很好的开始,但是还有更多工作需要完成以使Contoso Cookbook应用程序成为一流的Windows应用商店应用。这一旅程将在实验2中继续!