最近上网,看到Window8的新闻满天飞,看到Metro效果还不错,折腾了一个晚上把Windows8给装好了,比较PC屏幕大,个人感觉比WindowsPhone上面的效果更好,软件也都能装,用了用感觉还不错!这不闲着无事, 花了2个星期做了个编程在线Windows8 客户端!
文章概要:
1、熟悉INotifyPropertyChanged的使用
2、Style样式模板定义
3、ListView数据绑定
4、MessageDialog弹出窗口的使用
1、熟悉INotifyPropertyChanged的使用
在WPF开发中,数据绑定最经典的就是MVVM.数据绑定使用了ObservableCollection<T> 类来实现,ViewModel通过继承GalaSoft.MvvmLight.ViewModelBase类来实现,Command使用GalaSoft.MvvmLight.Command.RelayCommand<T>来实现。ObservableCollection<T>表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。在Window8 Metro开发中, 数据载体实体类通过实现INotifyPropertyChanged, 属性值变化时,自动更新UI(观察者模式)。
INotifyPropertyChanged 接口用于向客户端(通常是执行绑定的客户端)发出某一属性值已更改的通知。
-
实现 INotifyPropertyChanged 接口(首选)。 -
为绑定类型的每个属性提供更改事件。
INotifyPropertyChanged 原型实现:event PropertyChangedEventHandler PropertyChanged
6 接口如下:
7
8 namespace System.ComponentModel
9 {
10 public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
11
12 public interface INotifyPropertyChanged
13 {
14 event PropertyChangedEventHandler PropertyChanged;
15 }
16
17 public class PropertyChangedEventArgs : EventArgs
18 {
19 public PropertyChangedEventArgs(string propertyName);
20 public virtual string PropertyName { get; }
21 }
22 }
23 从数据对象到UI界面:当实现了INotifyPropertyChanged接口的对象有所改变时,会激发OnPropertyChanged这个接口方法,该方法保证了UI界面的数据同步。
下面就定义Article数据载体实体类,
2 {
3 /// <summary>
4 /// 说明: 文章实体类,实现INotifyPropertyChanged接口
5 /// 作者: Blue Sky
6 /// 时间:2012-11-05
7 /// </summary>
8 public class Article : INotifyPropertyChanged
9 {
10 private static Uri _baseUri = new Uri("ms-appx:///");
11
12 private int id;
13
14 public int Id
15 {
16 get { return this.id; }
17 set
18 {
19 this.id = value;
20 NotifyPropertyChanged("Id");
21 }
22
23 }
24
25 // 标题
26 private string title;
27 public string Title
28 {
29 get { return this.title; }
30 set
31 {
32 this.title = value;
33 NotifyPropertyChanged("Title");
34 }
35
36 }
37
38 private string imagePath = null;
39 public string ImagePath
40 {
41 get { return this.imagePath; }
42 set
43 {
44 this.imagePath = value;
45 NotifyPropertyChanged("ImagePath");
46 }
47
48 }
49
50 private ImageSource image = null;
51 public ImageSource Image
52 {
53 get
54 {
55 if (this.image == null && this.imagePath != null)
56 {
57 this.image = new Windows.UI.Xaml.Media.Imaging.BitmapImage(new Uri(_baseUri, this.imagePath));
58 }
59 return this.image;
60 }
61
62 set
63 {
64 this.image = value;
65 this.NotifyPropertyChanged("ImagePath");
66 }
67 }
68
69 private string description;
70
71 public string Description
72 {
73 get { return this.description; }
74 set
75 {
76 this.description = value;
77 NotifyPropertyChanged("Description");
78 }
79
80 }
81
82 // 文章内容
83 private string content;
84
85 public string Content
86 {
87 get { return this.content; }
88 set
89 {
90 this.content = value;
91 NotifyPropertyChanged("Content");
92 }
93
94 }
95
96 private DateTime createDate;
97 public DateTime CreateDate
98 {
99 get { return this.createDate; }
100 set
101 {
102 this.createDate = value;
103 NotifyPropertyChanged("CreateDate");
104 }
105
106 }
107
108 // 接口方法属性变更通知实现
109 public event PropertyChangedEventHandler PropertyChanged;
110
111 private void NotifyPropertyChanged(string propertyName)
112 {
113 if (PropertyChanged != null)
114 {
115 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
116 }
117 }
118
119 }
120 }
Article实体类
2 /// 页面数据视图对象,实现属性变更事件接口,自动更新UI
3 /// </summary>
4 public class ArticleViewModel : INotifyPropertyChanged
5 {
6 private ObservableCollection<Article> articleList;
7 public ObservableCollection<Article> ArticleList
8 {
9 get { return articleList; }
10 }
11
12 private int selectedItemIndex;
13 public int SelectedItemIndex
14 {
15 get { return selectedItemIndex; }
16 set { selectedItemIndex = value; NotifyPropertyChanged("SelectedItemIndex"); }
17 }
18
19 private Article selectedArticle;
20
21 public Article SelectedArticle
22 {
23 get{return this.selectedArticle;}
24 set
25 {
26 this.selectedArticle = value;
27 NotifyPropertyChanged("SelectedArticle");
28 }
29 }
30
31
32 public ArticleViewModel()
33 {
34 this.InitArticleData();
35 }
36
37 public event PropertyChangedEventHandler PropertyChanged;
38 private void NotifyPropertyChanged(string propertyName)
39 {
40 if (PropertyChanged != null)
41 {
42 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
43 }
44 }
45 }
2、Style样式模板定义
页面空间样式定义可以用两种方式:一种直接在对应XAML页面直接对控件属性定义,这个与HTML CSS 样式一样,一种是把样式定义成模板形式,把一些公共样式定义到一个文件中去,这个与CSS 样式表一样。在通过Visual Studio Express for Widows8 新建项目时,项目会自动生成一个公共的样式文件StandardStyles.xaml,这种方式也是推荐使用的一种方式,可以做到样式表统一,维护简单方便!
<Setter Property="FontWeight" Value="SemiLight"/>
</Style>
<!-- TextBlock 样式-->
<Style x:Key="BasicTextStyle" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource ApplicationForegroundThemeBrush}"/>
<Setter Property="FontSize" Value="{StaticResource ControlContentThemeFontSize}"/>
<Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}"/>
<Setter Property="TextTrimming" Value="WordEllipsis"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Typography.StylisticSet20" Value="True"/>
<Setter Property="Typography.DiscretionaryLigatures" Value="True"/>
<Setter Property="Typography.CaseSensitiveForms" Value="True"/>
</Style>
一看就一目了然,Style里面key就是给页面引用的,每一个Style都有唯一的一个key,同时指定是哪种控件类型,样式可以通过BasedOn关键字继承,property关键字是对空间每个属性值进行设置!这个公共样式引入是在App.xaml文件中引入的,每个Metro项目都有一个App.xaml文件。
使用方式: <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
〉〉再就是直接在页面定义样式,这种适合使用比较特殊的样式或者使用频率比较少场景.
例如直接定义图片的宽度和高度,:<Image Margin="0,0,20,0" Width="150" Height="150" Source="{Binding SelectedArticle.Image}" Stretch="UniformToFill"/>
3、ListView数据绑定
ListView控件定义可以方式可以通过两种方式进行
1、数据目标直接定义页面当中,如下:
2 ItemsSource="{Binding ArticleList}" SelectionChanged="Item_click" >
3 <ListView.ItemTemplate>
4 <DataTemplate>
5 <Grid Height="110" Margin="6">
6 <Grid.ColumnDefinitions>
7 <ColumnDefinition Width="Auto"/>
8 <ColumnDefinition Width="*"/>
9 </Grid.ColumnDefinitions>
10 <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
11 <Image Source="{Binding Image}" Stretch="UniformToFill"/>
12 </Border>
13 <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
14 <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
15 <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
16 </StackPanel>
17 </Grid>
18 </DataTemplate>
19 </ListView.ItemTemplate>
20 </ListView>
2、数据模板抽取到文件StandardStyles.xaml当中去,ListView中直接想引用样式一样使用。通过Listview的ItemTemplate属性执行数据模板。如下:
x:Name="itemListView"
AutomationProperties.AutomationId="ItemsListView"
AutomationProperties.Name="Items"
TabIndex="1"
Grid.Row="1"
Margin="-10,-10,0,0"
Padding="60,0,0,60"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}"
IsSwipeEnabled="False"
SelectionChanged="ItemListView_SelectionChanged"
ItemTemplate="{StaticResource Standard130ItemTemplate}"/>
2 <Grid Height="130" Margin="6">
3 <Grid.ColumnDefinitions>
4 <ColumnDefinition Width="Auto"/>
5 <ColumnDefinition Width="*"/>
6 </Grid.ColumnDefinitions>
7 <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}" Width="110" Height="110">
8 <Image Source="{Binding Image}" Stretch="UniformToFill"/>
9 </Border>
10 <StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
11 <TextBlock Text="{Binding Title}" Style="{StaticResource TitleTextStyle}" TextWrapping="NoWrap"/>
12 <TextBlock Text="{Binding Subtitle}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap"/>
13 <TextBlock Text="{Binding Description}" Style="{StaticResource BodyTextStyle}" MaxHeight="60"/>
14 </StackPanel>
15 </Grid>
16 </DataTemplate>
你看,模板方式定义简单,而且又简洁明了,而且动态加载样式时非常方便。
4、MessageDialog 弹出对话框实现
//弹出带有确定按钮的对话框
2 msgDialog.Commands.Add(new UICommand("确定", new UICommandInvokedHandler(OnUICommand)));
3 await msgDialog.ShowAsync();
在实例化UICommand时,我们使用了以下构造函数。
public UICommand(string label, UICommandInvokedHandler action);
指定一个与UICommandInvokedHandler委托绑定的方法,这样,当某个UICommand被用户单击后,会调用UICommandInvokedHandler绑定的对应方法,在本例中,所有UICommand都绑定到同一个方法。
此外,MessageDialog有两个属性应当注意一下:
1、CancelCommandIndex:默认“取消”按钮的索引,这个索引是对应于Commands中添加的UICommand的索引,从0开始,按添加顺序,第一个UICommand的索引为0,第二个UICommand的索引为1,第三个为2,依此类推(当然,最多就只有三个,索引2)。假如CancelCommandIndex属性设置了1,那么,消息框中的第二个按钮就是默认的“取消”命令,只要按下ESC键就能触发。
2、DefaultCommandIndex:默认“确定”指令的索引,例如设置为0,即Commands中第一个按钮为默认命令,只要按下回车键就能触发。
要显示MessageDialog,调用ShowAsync方法,注意这个方法是异步方法,要用await关键字,同时,凡是调用了异步方法并加有await关键字的方法,在定义时还要加上async关键字
2 msg.Commands.Add(new UICommand("重试", new UICommandInvokedHandler(OnUICommand)));
3 msg.Commands.Add(new UICommand("忽略", new UICommandInvokedHandler(OnUICommand)));
4 msg.Commands.Add(new UICommand("取消", new UICommandInvokedHandler(OnUICommand)));
5 // 默认按钮索引
6 msg.DefaultCommandIndex = 0;
7 msg.CancelCommandIndex = 2;
2 {
3 /// <summary>
4 /// 可用于自身或导航至 Frame 内部的空白页。
5 /// </summary>
6 public sealed partial class MainPage : Page
7 {
8 private ArticleViewModel viewModel;
9
10 public MainPage()
11 {
12 this.InitializeComponent();
13
14 viewModel = new ArticleViewModel();
15 // Page默认上下文绑定数据源
16 this.DataContext = viewModel;
17 //或者直接绑定ListView
18 //this.lvArticles.ItemsSource = viewModel;
19 //委托绑定集合列表变化时触发动作
20 viewModel.ArticleList.CollectionChanged += ArticleList_CollectionChanged;
21 }
22
23 /// <summary>
24 /// 文章列表发生变化事件,用到了MessageDialog 弹出框
25 /// </summary>
26 /// <param name="sender"></param>
27 /// <param name="e"></param>
28 async void ArticleList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
29 {
30 // 弹出带有确定按钮的对话框
31 MessageDialog msgDialog = new MessageDialog("文章列表发生变化");
32 msgDialog.Commands.Add(new UICommand("确定", new UICommandInvokedHandler(OnUICommand)));
33 await msgDialog.ShowAsync();
34 }
35
36 /// <summary>
37 /// 单击确定时回调事件
38 /// </summary>
39 /// <param name="cmd"></param>
40 async void OnUICommand(IUICommand cmd)
41 {
42 await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
43 {
44
45 });
46 }
47
48 /// <summary>
49 /// ListView 选中触发事件
50 /// </summary>
51 /// <param name="sender"></param>
52 /// <param name="e"></param>
53 private void Item_click(object sender, SelectionChangedEventArgs e)
54 {
55 if (e.AddedItems.Count > 0)
56 {
57 Article selectedItem = e.AddedItems[0] as Article;
58 if (selectedItem != null)
59 {
60 // 获取选中文章,展示文章详细信息,因SelectedArticle 属性实现了NotifyPropertyChanged事件,当SelectedArticle值发生变化时,自动更新UI
61 viewModel.SelectedArticle = selectedItem;
62 // Webview显示HTML脚本,暂时没发现Webview直接绑定显示HMTL的,只能直接赋值
63 ContentView.NavigateToString(selectedItem.Content);
64 }
65 }
66 }
67 }
68 }
好了,晚了,今天就写到这了,下一篇准备写一下Window8 Metro开发之数据存储!