zoukankan      html  css  js  c++  java
  • 通用Windows应用《博客园-开发者的网上家园》开发(1)——MVVM模式

    最近开发了个WP8.1和Windows8.1平台上的应用——《博客园-开发者的网上家园》,基于 Windows Runtime 。在此有必要说明一下,WP8.0以前的应用程序是基于Silverlight的,微软为了统一Windows Phone OS 和 Windows RT,从开发人员的角度上,也统一了两个平台上大部分的API,使得开发人员可以共享代码(而不是一次编写,跨平台运行)。

    本文着重描述MVVM在Windows Runtime应用程序下的表现,关于MVVM模式的理解,可参考园子里 天神一 的博客——《MVVM架构的简单解析》。

    来看Model的代码

     1     public class Blog
     2     {
     3         /// <summary>
     4         /// 博客Id
     5         /// </summary>
     6         public long Id { get; set; }
     7         /// <summary>
     8         /// 博客标题
     9         /// </summary>
    10         public string Title { get; set; }
    11         /// <summary>
    12         /// 博客摘要
    13         /// </summary>
    14         public string Summary { get; set; }
    15         /// <summary>
    16         /// 博客发表时间
    17         /// </summary>
    18         public string Published { get; set; }
    19         /// <summary>
    20         /// 博客更新时间
    21         /// </summary>
    22         public string Updated { get; set; }
    23         /// <summary>
    24         /// 博主
    25         /// </summary>
    26         public Author Author { get; set; }
    27         /// <summary>
    28         /// 博客链接
    29         /// </summary>
    30         public string Link { get; set; }
    31         /// <summary>
    32         /// 博主博客名称
    33         /// </summary>
    34         public string BlogApp { get; set; }
    35         /// <summary>
    36         /// 推荐数
    37         /// </summary>
    38         public int Diggs { get; set; }
    39         /// <summary>
    40         /// 阅读数
    41         /// </summary>
    42         public int Views { get; set; }
    43         /// <summary>
    44         /// 评论数
    45         /// </summary>
    46         public int Comments { get; set; }
    47     }
    Blog
        public class Author
        {
            /// <summary>
            /// 博主名字
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 博主博客链接
            /// </summary>
            public string Uri { get; set; }
            /// <summary>
            /// 博主头像地址
            /// </summary>
            public string Avatar { get; set; }
        }
    Author

    再看ViewModel

     1 public class ViewModel : INotifyPropertyChanged
     2     {
     3         public ViewModel()
     4         {
     5             this.Blogs = new ObservableCollection<Blog>();
     6         }
     7 
     8         /// <summary>
     9         /// 加载某一页博客
    10         /// </summary>
    11         /// <param name="pageIndex">页码</param>
    12         /// <param name="pageSize">多少条</param>
    13         /// <returns></returns>
    14         public async Task LoadBlogsAsync(int pageIndex, int pageSize)
    15         {
    16             string url = string.Format(BlogUrl, pageIndex, pageSize);
    17             this.Blogs = XmlHelper.XmlToBlog(await new HttpClient().GetStringAsync(new Uri(url)));
    18             IsBlogLoaded = true;
    19         }
    20 
    21         /// <summary>
    22         /// 加载下一页博客,并追加到当前数据源中
    23         /// </summary>
    24         /// <param name="count">加载多少条</param>
    25         /// <returns></returns>
    26         public async Task LoadMoreBlogsAsync(int count)
    27         {
    28             string url = string.Format(BlogUrl, ++App.CurrentBlogPage, count);
    29             foreach (var item in XmlHelper.XmlToBlog(await new HttpClient().GetStringAsync(new Uri(url))))
    30             {
    31                 this.Blogs.Add(item);
    32             }
    33         }
    34 
    35         private ObservableCollection<Blog> _blogs;
    36         public ObservableCollection<Blog> Blogs
    37         {
    38             get { return _blogs; }
    39             private set
    40             {
    41                 if (value != _blogs)
    42                 {
    43                     _blogs = value;
    44                     NotifyPropertyChanged("Blogs");
    45                 }
    46             }
    47         }
    48 
    49         private const string BlogUrl = "http://wcf.open.cnblogs.com/blog/sitehome/paged/{0}/{1}";
    50 
    51         public bool IsLoaded { get; private set; }
    52 
    53         public event PropertyChangedEventHandler PropertyChanged;
    54         private void NotifyPropertyChanged(string propertyName)
    55         {
    56             PropertyChangedEventHandler handler = PropertyChanged;
    57             if (null != handler)
    58             {
    59                 handler(this, new PropertyChangedEventArgs(propertyName));
    60             }
    61         }
    62     }
    ViewModel

    先看看 INotifyPropertyChanged 这个接口的定义:

    INotifyPropertyChanged接口定义

    这个接口只定义了一个事件:PropertyChanged,属性改变。接口的说明告诉我们,这个接口的作用在于当我用于绑定在UI上的数据源发生改变的时候,可以向界面发出通知,让界面做出相应的改变。

    ViewModel实现了INotifyPropertyChanged接口,当ViewModel改变时可以通知UI做出相应的改变,同时,不使用List<T>作为数据集,而是使用ObservableCollection<T>,看看ObservableCollection<T>的定义:

      1     // 摘要: 
      2     //     表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。
      3     //
      4     // 类型参数: 
      5     //   T:
      6     //     集合中的元素类型。
      7     public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
      8     {
      9         // 摘要: 
     10         //     初始化 System.Collections.ObjectModel.ObservableCollection<T> 类的新实例。
     11         public ObservableCollection();
     12         //
     13         // 摘要: 
     14         //     初始化 System.Collections.ObjectModel.ObservableCollection<T> 类的新实例,该类包含从指定集合中复制的元素。
     15         //
     16         // 参数: 
     17         //   collection:
     18         //     从中复制元素的集合。
     19         //
     20         // 异常: 
     21         //   System.ArgumentNullException:
     22         //     collection 参数不能为 null。
     23         public ObservableCollection(IEnumerable<T> collection);
     24 
     25         // 摘要: 
     26         //     在添加、移除、更改或移动项或者在刷新整个列表时发生。
     27         public virtual event NotifyCollectionChangedEventHandler CollectionChanged;
     28         //
     29         // 摘要: 
     30         //     在属性值更改时发生。
     31         protected virtual event PropertyChangedEventHandler PropertyChanged;
     32 
     33         // 摘要: 
     34         //     不允许可重入的更改此集合的尝试。
     35         //
     36         // 返回结果: 
     37         //     可用于释放对象的 System.IDisposable 对象。
     38         protected IDisposable BlockReentrancy();
     39         //
     40         // 摘要: 
     41         //     检查可重入的更改此集合的尝试。
     42         //
     43         // 异常: 
     44         //   System.InvalidOperationException:
     45         //     如果存在对 System.Collections.ObjectModel.ObservableCollection<T>.BlockReentrancy()(尚未释放其
     46         //     System.IDisposable 返回值)的调用。 通常,这意味着在 System.Collections.ObjectModel.ObservableCollection<T>.CollectionChanged
     47         //     事件期间进行了额外的更改此集合的尝试。 但是,这取决于派生类何时选择调用 System.Collections.ObjectModel.ObservableCollection<T>.BlockReentrancy()。
     48         protected void CheckReentrancy();
     49         //
     50         // 摘要: 
     51         //     从集合中移除所有项。
     52         protected override void ClearItems();
     53         //
     54         // 摘要: 
     55         //     将一项插入集合中指定索引处。
     56         //
     57         // 参数: 
     58         //   index:
     59         //     从零开始的索引,应在该位置插入 item。
     60         //
     61         //   item:
     62         //     要插入的对象。
     63         protected override void InsertItem(int index, T item);
     64         //
     65         // 摘要: 
     66         //     将指定索引处的项移至集合中的新位置。
     67         //
     68         // 参数: 
     69         //   oldIndex:
     70         //     从零开始的索引,用于指定要移动的项的位置。
     71         //
     72         //   newIndex:
     73         //     从零开始的索引,用于指定项的新位置。
     74         public void Move(int oldIndex, int newIndex);
     75         //
     76         // 摘要: 
     77         //     将指定索引处的项移至集合中的新位置。
     78         //
     79         // 参数: 
     80         //   oldIndex:
     81         //     从零开始的索引,用于指定要移动的项的位置。
     82         //
     83         //   newIndex:
     84         //     从零开始的索引,用于指定项的新位置。
     85         protected virtual void MoveItem(int oldIndex, int newIndex);
     86         //
     87         // 摘要: 
     88         //     引发带有提供的参数的 System.Collections.ObjectModel.ObservableCollection<T>.CollectionChanged
     89         //     事件。
     90         //
     91         // 参数: 
     92         //   e:
     93         //     要引发的事件的参数。
     94         protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e);
     95         //
     96         // 摘要: 
     97         //     引发带有提供的参数的 System.Collections.ObjectModel.ObservableCollection<T>.PropertyChanged
     98         //     事件。
     99         //
    100         // 参数: 
    101         //   e:
    102         //     要引发的事件的参数。
    103         protected virtual void OnPropertyChanged(PropertyChangedEventArgs e);
    104         //
    105         // 摘要: 
    106         //     移除集合中指定索引处的项。
    107         //
    108         // 参数: 
    109         //   index:
    110         //     要移除的元素的从零开始的索引。
    111         protected override void RemoveItem(int index);
    112         //
    113         // 摘要: 
    114         //     替换指定索引处的元素。
    115         //
    116         // 参数: 
    117         //   index:
    118         //     待替换元素的从零开始的索引。
    119         //
    120         //   item:
    121         //     位于指定索引处的元素的新值。
    122         protected override void SetItem(int index, T item);
    123     }
    ObservableCollection<T>

    ObservableCollection<T>集成于Collection<T>,同时实现了两个接口:INotifyCollectionChanged 和 INotifyPropertyChanged,前者用于通知UI数据集改变,后者用于通知UI数据集中的属性改变。

    另外在ViewModel中自定义了两个方法:LoadBlogsAsync(int pageIndex, int pageSize) 和 LoadMoreBlogsAsync(int count),都是异步方法。

    在App.xaml.cs里定义一个静态属性ViewModel,用于全局访问

     1     private static ViewModel viewModel = null;
     2     /// <summary>
     3     /// 视图用于进行绑定的静态 ViewModel。
     4     /// </summary>
     5     /// <returns>ViewModel 对象。</returns>
     6     public static ViewModel ViewModel
     7     {
     8         get
     9         {
    10             // 延迟创建视图模型,直至需要时
    11             if (viewModel == null)
    12                 viewModel = new ViewModel();
    13             return viewModel;
    14         }
    15     }
    ViewModel属性

    上面说到延迟加载,直至需要时。那么什么时候需要呢,当然是我们在页面上需要展示数据的时候。在MainPage.xaml的构造方法里,我们去创建ViewModel,并赋值给MainPage的数据上下文。

    1     public MainPage()
    2     {
    3         this.InitializeComponent();
    4         DataContext = App.ViewModel;
    5     }
    MainPage构造方法

    并在导航到该页面的时候加载ViewModel的数据:

    1     protected override async void OnNavigatedTo(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
    2     {
    3         MyProgressBar.Visibility = Visibility.Visible;
    4         if (!App.ViewModel.IsLoaded)
    5         {
    6             await App.ViewModel.LoadBlogsAsync(1, App.PageSizeBlog);
    7         }
    8         MyProgressBar.Visibility = Visibility.Collapsed;
    9     }
    OnNavigatedTo

    上面便实现了所谓的延时加载。

    剩下的便是如何在UI上绑定了

     1  <ListBox Loaded="GridViewData_Loaded" SelectionChanged="GridViewData_SelectionChanged" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Foreground="{ThemeResource ApplicationForegroundThemeBrush}" Name="GridViewData" ItemsSource="{Binding Blogs,Mode=TwoWay}">
     2                     <ListBox.ItemTemplate>
     3                         <DataTemplate>
     4                             <StackPanel>
     5                                 <TextBlock FontSize="18" Text="{Binding Title}" TextWrapping="Wrap"></TextBlock>
     6                                 <StackPanel Orientation="Horizontal" Margin="0 12">
     7                                     <TextBlock Text="{Binding Author.Name}" Foreground="#FF2B6695"></TextBlock>
     8                                     <TextBlock Text="发布于" Margin="6 0"></TextBlock>
     9                                     <TextBlock Text="{Binding Published}" Margin="0 0 6 0"></TextBlock>
    10                                     <TextBlock Text="评论("></TextBlock>
    11                                     <TextBlock Text="{Binding Comments}"></TextBlock>
    12                                     <TextBlock Text=")" Margin="0 0 6 0"></TextBlock>
    13                                     <TextBlock Text="阅读("></TextBlock>
    14                                     <TextBlock Text="{Binding Views}"></TextBlock>
    15                                     <TextBlock Text=")"></TextBlock>
    16                                 </StackPanel>
    17                                 <Grid HorizontalAlignment="Left">
    18                                     <Grid.ColumnDefinitions>
    19                                         <ColumnDefinition Width="Auto"/>
    20                                         <ColumnDefinition/>
    21                                     </Grid.ColumnDefinitions>
    22                                     <Image HorizontalAlignment="Left" Width="48" Height="48" Source="{Binding Author.Avatar}" Grid.Column="0" VerticalAlignment="Top"/>
    23                                     <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Padding="12 0" Grid.Column="1" Text="{Binding Summary}"></TextBlock>
    24                                 </Grid>
    25                                 <Canvas Background="#FF2B6695" Height="2" Width="1600" Margin="0 12 12 0"/>
    26                             </StackPanel>
    27                         </DataTemplate>
    28                     </ListBox.ItemTemplate>
    29                 </ListBox>
    MainPage.xaml

    布局可无视。

    下面是广告时间,凭良心进。

    windows 应用商店app(windows 8.1 +):http://apps.microsoft.com/windows/zh-cn/app/4f20c8c7-2dfa-4e93-adcb-87acde53d4be

    windows phone 应用商店app(windows phone 8.1 +):http://www.windowsphone.com/s?appid=71e79c48-ad5d-4563-a42f-06d59d969eb8

    第一版功能比较鸡肋,后续版本将添加更多功能。如果有什么好的建议的,希望在商店里提出,或者博客里留言,我将综合各方意见打造一个体验更好的win平台的博客园app。

    最后晒下图吧:

  • 相关阅读:
    python_函数
    初始python第三天(三)
    python入门练习题2
    python开发进阶之路(一)
    python入门练习题1
    初识Python第三天(二)
    初识Python第三天(一)
    初识Python第二天(4)
    初识python第二天(3)
    c windows控制台输出颜色文字
  • 原文地址:https://www.cnblogs.com/rainlam163/p/3898387.html
Copyright © 2011-2022 走看看