通用Windows应用《博客园-开发者的网上家园》开发(1)——MVVM模式
最近開發(fā)了個WP8.1和Windows8.1平臺上的應(yīng)用——《博客園-開發(fā)者的網(wǎng)上家園》,基于?Windows Runtime?。在此有必要說明一下,WP8.0以前的應(yīng)用程序是基于Silverlight的,微軟為了統(tǒng)一Windows Phone OS 和 Windows RT,從開發(fā)人員的角度上,也統(tǒng)一了兩個平臺上大部分的API,使得開發(fā)人員可以共享代碼(而不是一次編寫,跨平臺運行)。
本文著重描述MVVM在Windows Runtime應(yīng)用程序下的表現(xiàn),關(guān)于MVVM模式的理解,可參考園子里?天神一?的博客——《MVVM架構(gòu)的簡單解析》。
來看Model的代碼
1 public class Blog 2 { 3 /// <summary> 4 /// 博客Id 5 /// </summary> 6 public long Id { get; set; } 7 /// <summary> 8 /// 博客標(biāo)題 9 /// </summary> 10 public string Title { get; set; } 11 /// <summary> 12 /// 博客摘要 13 /// </summary> 14 public string Summary { get; set; } 15 /// <summary> 16 /// 博客發(fā)表時間 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 /// 推薦數(shù) 37 /// </summary> 38 public int Diggs { get; set; } 39 /// <summary> 40 /// 閱讀數(shù) 41 /// </summary> 42 public int Views { get; set; } 43 /// <summary> 44 /// 評論數(shù) 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 /// 加載下一頁博客,并追加到當(dāng)前數(shù)據(jù)源中 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,屬性改變。接口的說明告訴我們,這個接口的作用在于當(dāng)我用于綁定在UI上的數(shù)據(jù)源發(fā)生改變的時候,可以向界面發(fā)出通知,讓界面做出相應(yīng)的改變。
ViewModel實現(xiàn)了INotifyPropertyChanged接口,當(dāng)ViewModel改變時可以通知UI做出相應(yīng)的改變,同時,不使用List<T>作為數(shù)據(jù)集,而是使用ObservableCollection<T>,看看ObservableCollection<T>的定義:
1 // 摘要: 2 // 表示一個動態(tài)數(shù)據(jù)集合,在添加項、移除項或刷新整個列表時,此集合將提供通知。 3 // 4 // 類型參數(shù): 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> 類的新實例,該類包含從指定集合中復(fù)制的元素。 15 // 16 // 參數(shù): 17 // collection: 18 // 從中復(fù)制元素的集合。 19 // 20 // 異常: 21 // System.ArgumentNullException: 22 // collection 參數(shù)不能為 null。 23 public ObservableCollection(IEnumerable<T> collection); 24 25 // 摘要: 26 // 在添加、移除、更改或移動項或者在刷新整個列表時發(fā)生。 27 public virtual event NotifyCollectionChangedEventHandler CollectionChanged; 28 // 29 // 摘要: 30 // 在屬性值更改時發(fā)生。 31 protected virtual event PropertyChangedEventHandler PropertyChanged; 32 33 // 摘要: 34 // 不允許可重入的更改此集合的嘗試。 35 // 36 // 返回結(jié)果: 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 返回值)的調(diào)用。 通常,這意味著在 System.Collections.ObjectModel.ObservableCollection<T>.CollectionChanged 47 // 事件期間進(jìn)行了額外的更改此集合的嘗試。 但是,這取決于派生類何時選擇調(diào)用 System.Collections.ObjectModel.ObservableCollection<T>.BlockReentrancy()。 48 protected void CheckReentrancy(); 49 // 50 // 摘要: 51 // 從集合中移除所有項。 52 protected override void ClearItems(); 53 // 54 // 摘要: 55 // 將一項插入集合中指定索引處。 56 // 57 // 參數(shù): 58 // index: 59 // 從零開始的索引,應(yīng)在該位置插入 item。 60 // 61 // item: 62 // 要插入的對象。 63 protected override void InsertItem(int index, T item); 64 // 65 // 摘要: 66 // 將指定索引處的項移至集合中的新位置。 67 // 68 // 參數(shù): 69 // oldIndex: 70 // 從零開始的索引,用于指定要移動的項的位置。 71 // 72 // newIndex: 73 // 從零開始的索引,用于指定項的新位置。 74 public void Move(int oldIndex, int newIndex); 75 // 76 // 摘要: 77 // 將指定索引處的項移至集合中的新位置。 78 // 79 // 參數(shù): 80 // oldIndex: 81 // 從零開始的索引,用于指定要移動的項的位置。 82 // 83 // newIndex: 84 // 從零開始的索引,用于指定項的新位置。 85 protected virtual void MoveItem(int oldIndex, int newIndex); 86 // 87 // 摘要: 88 // 引發(fā)帶有提供的參數(shù)的 System.Collections.ObjectModel.ObservableCollection<T>.CollectionChanged 89 // 事件。 90 // 91 // 參數(shù): 92 // e: 93 // 要引發(fā)的事件的參數(shù)。 94 protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e); 95 // 96 // 摘要: 97 // 引發(fā)帶有提供的參數(shù)的 System.Collections.ObjectModel.ObservableCollection<T>.PropertyChanged 98 // 事件。 99 // 100 // 參數(shù): 101 // e: 102 // 要引發(fā)的事件的參數(shù)。 103 protected virtual void OnPropertyChanged(PropertyChangedEventArgs e); 104 // 105 // 摘要: 106 // 移除集合中指定索引處的項。 107 // 108 // 參數(shù): 109 // index: 110 // 要移除的元素的從零開始的索引。 111 protected override void RemoveItem(int index); 112 // 113 // 摘要: 114 // 替換指定索引處的元素。 115 // 116 // 參數(shù): 117 // index: 118 // 待替換元素的從零開始的索引。 119 // 120 // item: 121 // 位于指定索引處的元素的新值。 122 protected override void SetItem(int index, T item); 123 } ObservableCollection<T>ObservableCollection<T>集成于Collection<T>,同時實現(xiàn)了兩個接口:INotifyCollectionChanged 和 INotifyPropertyChanged,前者用于通知UI數(shù)據(jù)集改變,后者用于通知UI數(shù)據(jù)集中的屬性改變。
另外在ViewModel中自定義了兩個方法:LoadBlogsAsync(int pageIndex, int pageSize) 和?LoadMoreBlogsAsync(int count),都是異步方法。
在App.xaml.cs里定義一個靜態(tài)屬性ViewModel,用于全局訪問
1 private static ViewModel viewModel = null; 2 /// <summary> 3 /// 視圖用于進(jìn)行綁定的靜態(tài) ViewModel。 4 /// </summary> 5 /// <returns>ViewModel 對象。</returns> 6 public static ViewModel ViewModel 7 { 8 get 9 { 10 // 延遲創(chuàng)建視圖模型,直至需要時 11 if (viewModel == null) 12 viewModel = new ViewModel(); 13 return viewModel; 14 } 15 } ViewModel屬性上面說到延遲加載,直至需要時。那么什么時候需要呢,當(dāng)然是我們在頁面上需要展示數(shù)據(jù)的時候。在MainPage.xaml的構(gòu)造方法里,我們?nèi)?chuàng)建ViewModel,并賦值給MainPage的數(shù)據(jù)上下文。
1 public MainPage() 2 { 3 this.InitializeComponent(); 4 DataContext = App.ViewModel; 5 } MainPage構(gòu)造方法并在導(dǎo)航到該頁面的時候加載ViewModel的數(shù)據(jù):
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上面便實現(xiàn)了所謂的延時加載。
剩下的便是如何在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="發(fā)布于" 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布局可無視。
?
下面是廣告時間,憑良心進(jìn)。
?
windows 應(yīng)用商店app(windows 8.1 +):http://apps.microsoft.com/windows/zh-cn/app/4f20c8c7-2dfa-4e93-adcb-87acde53d4be
windows phone 應(yīng)用商店app(windows phone 8.1 +):http://www.windowsphone.com/s?appid=71e79c48-ad5d-4563-a42f-06d59d969eb8
第一版功能比較雞肋,后續(xù)版本將添加更多功能。如果有什么好的建議的,希望在商店里提出,或者博客里留言,我將綜合各方意見打造一個體驗更好的win平臺的博客園app。
?
最后曬下圖吧:
?
轉(zhuǎn)載于:https://www.cnblogs.com/rainlam163/p/3898387.html
總結(jié)
以上是生活随笔為你收集整理的通用Windows应用《博客园-开发者的网上家园》开发(1)——MVVM模式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 8条腾讯的产品管理方式
- 下一篇: 运维的85条军规