本文学习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属性,可惜这个属性并未在文档中标出。
其他参考
- {x:Bind} markup extension
- Data binding in depth
- Proper way to use CollectionViewSource in ViewModel
- UWP — SemanticZoom
- Windows 8.x App市集應用程式開發–使用C#(電子書)/如何分组资料
(完)