本文学习UWP的两个样例:Bookstore1和Bookstore2。

样例链接:

如果在Visual Studio 2019里面编译上面两个例子出错,可以先更新一下其NuGet包:Microsoft.NETCore.UniversalWindowsPlatform。

Bookstore1

  • App.xaml,无甚内容
  • MainPage.xaml,主要XAML模块
  • BookstoreStyles.xaml,定义了样式

BookstoreStyles.xaml主要用于定义样式,然后在其他XAML(MainPage.xaml)中引用,如下所示:

<Page.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="BookstoreStyles.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    ...
    </ResourceDictionary>
    ...
</Page.Resources>

Page的DataContext设置为:

    <Page.DataContext>
        <Bookstore1Universal_10:BookstoreViewModel/>
    </Page.DataContext>

其中BookstoreViewModel类在BookstoreViewModel.cs中定义。为了符合上面的XAML代码片段,BookstoreViewModel的构造参数是不带参数的。BookstoreViewModel类有三个只读格属(property):

  • string AppName
  • ObservableCollection BookSkus
  • string PageTitle

其中BookSkus是在构造参数中填充的,使用了一些测试用的数据。测试数据的类型是BookSku类型,有Author、Title、CoverImage等格属。

不知道为什么,BookSku的格属是指定为{get, internal_set}的?

Windows.ApplicationModel.DesignMode.DesignModeEnabled格属可以用来判断当前是否处于设计模式,可能跟Sample data on the design surface, and for prototyping描述的相关。

在C#中可以使用#region xxx#endregion xxx这样的语法来创建缩进标识,VisualStudio会识别相应的标识并折叠代码。

MainPage.xaml的布局采用两栏的Grid,上一栏显示标题,由子元素决定高度;,下一栏显示内容。 内容由一个ListBox构成,其ItemsSource来自于BookstoreViewModel的BookSkus格属,子项的显示由BookTemplate决定。BookTemplate是一个两列的Grid,左侧显示书籍的封面,右侧显示书籍的标题和简介。

XAML不支持循环,因为循环的话至少需要额外的变量来当前进度和终止条件这些状态,XAML是基于XML的,没有方便的方式指定变量(不像很多HTML模板可以有循环语句)。所以XAML应对的方式是采用数据模板,就是上面的BookTemplate。在碰到一个集合的时候(比如ListBox),XAML解析器会遍历这个集合,对每个子项应用指定的模板。这种方式,其实和函数式编程有点类似。函数式编程一般也没有循环,但是可以用递归和遍历来达到循环的效果。

C#有下面这种语法,可以方便设置对象的多个属性

			bookSkus.Add(new BookSku()
			{
				Title = "A Christmas Carol",
				Author = "Charles Dickens",
				CoverImagePath = "/Assets/CoverImages/one.png"
			});

Bookstore2

Bookstore2对Bookstore1做了一些改进。首先在BookstoreViewModel.cs里面通过新增Author类把BookSku和Author分开了。

	public class Author : IEnumerable<BookSku>
	{
		#region fields
		private static Dictionary<string, Author> authorDictionary = new Dictionary<string, Author>();
		private ObservableCollection<BookSku> bookSkus = new ObservableCollection<BookSku>();
		#endregion fields
        ...
 		#region IEnumerable<BookSku>
		public IEnumerator<BookSku> GetEnumerator()
		{
			return this.BookSkus.GetEnumerator();
		}
		#endregion IEnumerable<BookSku>

		#region IEnumerable
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return this.BookSkus.GetEnumerator();
		}
		#endregion IEnumerable
    }

同时新增了BookSku的构造函数,每当添加一个BookSku的时候,其Author信息就会被添加到Author的静态Dictionary中。

在XAML方面,Bookstore2使用一个自定义控件SeZoUC来显示内容。SeZoUC的XAML的根元素是UserControl,而不是传统的Page。在SeZoUC的ResourceDictionary中,CollectionViewSource元素(来自于Windows.UI.Xaml.Data)用来绑定Authors(获取自MainPage.xaml的DataContext的属格)。

同时MainPage.xaml的Grid采用了VisualStateManager来定义了宽窄两个显示状态,低于548像素则为窄,否则为宽。两种显示状态通过SemanticZoom来进行切换。显示宽的时候,使用GridView来显示内容;显示窄的时候使用ListView来显示。

ItemsSource="{Binding CollectionGroups, Source={StaticResource AuthorHasACollectionOfBookSku}}“表明CollectionViewSource有一个CollectionGroups属性,可惜这个属性并未在文档中标出。

其他参考

(完)