编程在线Windows8客户端实现之数据绑定

      最近上网,看到Window8的新闻满天飞,看到Metro效果还不错,折腾了一个晚上把Windows8给装好了,比较PC屏幕大,个人感觉比WindowsPhone上面的效果更好,软件也都能装,用了用感觉还不错!这不闲着无事, 花了2个星期做了个编程在线Windows8 客户端!

Title

编程在线网站:http://facejob.sinaapp.com/

编程在线Windows8 客户端:http://www.cnblogs.com/hubcarl/archive/2012/11/18/2776608.html

文章概要:

 

    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

数据绑定,就是要保持数据对象和UI界面的同步。NET事件绑定是基于Observer模式的。在.NET2.0中,对Observer进行了一次包装,可以引用System.Component命名空间,实现INotifyPropertyChanged接口,可以获得事件PropertyChanged,以及PropertyChangedEventArgs。于是在这套体系下,事件机制事先搭建好了。
     
  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数据载体实体类,实现INotifyPropertyChanged 接口,同时为绑定类型的每个属性提供更改事件。

 

ExpandedBlockStart.gif View Code
  1  namespace Windows8Study.Model
  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( thisnew PropertyChangedEventArgs(propertyName));
116             }
117         }
118 
119     }
120 }

Article实体类实现INotifyPropertyChanged 接口,同时为绑定类型的每个属性提供更改事件。下面就是要实现视图实体类ArticleViewModel,直接与界面进行交互对象。主要包含两个属性文章列表ArticleList和当前选中的文章SelectedArticle属性,因这两个属性值会变化,同时要更新UI,所以也要实现INotifyPropertyChanged 接口。

 

 1  ///   <summary>
 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( thisnew PropertyChangedEventArgs(propertyName));
43             }
44         }
45 }

 2、Style样式模板定义

        页面空间样式定义可以用两种方式:一种直接在对应XAML页面直接对控件属性定义,这个与HTML CSS 样式一样,一种是把样式定义成模板形式,把一些公共样式定义到一个文件中去,这个与CSS 样式表一样。在通过Visual Studio Express for Widows8 新建项目时,项目会自动生成一个公共的样式文件StandardStyles.xaml,这种方式也是推荐使用的一种方式,可以做到样式表统一,维护简单方便!

  < Style  x:Key ="BodyRichTextStyle"  TargetType ="RichTextBlock"  BasedOn ="{StaticResource BaselineRichTextStyle}" >
         < 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、数据目标直接定义页面当中,如下:

ExpandedBlockStart.gif View Code
 1   < ListView  Grid.Row ="0"  Grid.Column ="0"  x:Name ="lvArticles"  Height ="800"  Margin ="60,60,0,60"
 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属性执行数据模板。如下:

< ListView
            
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} " />

 Standard130ItemTemplate在文件中定义如下:

 

 1  < DataTemplate  x:Key ="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 弹出对话框实现

   //弹出带有确定按钮的对话框
   

1    MessageDialog msgDialog =  new  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关键字

           

1 MessageDialog msg =  new MessageDialog( " 按钮测试 ");
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;

 好了,讲了这么多,现在把这四点运用上,直接上代码了,比较简单。

 1  namespace Windows8Study
 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[ 0as 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开发之数据存储!

 

 

Title

编程在线网站:http://www.cnblogs.com/hubcarl/archive/2012/11/18/2776608.html

编程在线Windows8 客户端:http://www.cnblogs.com/hubcarl/archive/2012/11/18/2776608.html

转载于:https://www.cnblogs.com/hubcarl/archive/2012/11/22/CodeOnlive.html

猜你喜欢

转载自blog.csdn.net/weixin_33769125/article/details/93817334