编程在线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 接口,同时为绑定类型的每个属性提供更改事件。

 

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、数据目标直接定义页面当中,如下:

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/p/CodeOnlive.html