在前三章中咱們完成了登陸窗口, 並掌握了使用Conductor來切換窗口, 但這些其實都是在爲咱們的系統打基礎.html
而本章中咱們就要開始開發系統的核心功能, 即圖書管理功能了.git
經過本章, 咱們會接觸到如下知識點:github
讓咱們開始吧!數據庫
有朋友說咱們的系統界面有點簡陋, 有點辜負WPF的美名. 其實UI並非本系列文章主要關注的內容. 可是既然有朋友指出來, 那麼這裏就稍微美化一下UI, 這樣看起來也賞心悅目一些.編程
WPF的UI庫有不少, 這裏咱們使用一個頗有名的開源UI庫: Material Design In XAML.網絡
WPF使用UI庫是很簡單的, 這裏就不作過多說明了, 朋友們可直接看代碼. 使用後本章最終效果以下:app
MVVM中第一個M即爲Model的意思, 接下來咱們就爲圖書建立Model類, 作爲圖書信息的模型.ide
在工程中建立一個名爲"Models"的文件夾,並在該文件夾下建立Book
類和BookType
枚舉,分別表明圖書類和圖書類型枚舉:學習
Book
類內容以下:測試
/// <summary> /// 圖書 /// </summary> public class Book { /// <summary> /// 書名 /// </summary> public string Name { get; set; } /// <summary> /// 類型 /// </summary> public BookType Type { get; set; } /// <summary> /// 出版年月 /// </summary> public DateTime PublishDate { get; set; } /// <summary> /// 價格 /// </summary> public float Price { get; set; } /// <summary> /// 封面URL /// </summary> public string CoverUrl { get; set; } public Book(string name, BookType type, DateTime publishDate, float price, string coverUrl) { Name = name; Type = type; PublishDate = publishDate; Price = price; CoverUrl = coverUrl; } }
BookType
內容以下:
public enum BookType { /// <summary> /// 未定義 /// </summary> Undefined, /// <summary> /// 傳記 /// </summary> Biography, /// <summary> /// 奇幻 /// </summary> Fantastic, /// <summary> /// 恐怖 /// </summary> Horror, /// <summary> /// 科幻 /// </summary> ScienceFiction, /// <summary> /// 懸疑 /// </summary> Mystery, /// <summary> /// 編程 /// </summary> Programming, }
兩個文件內容都很簡單, 無需作過多解釋.
雖然咱們的簡易系統並不使用數據庫, 可是咱們仍然須要將圖書信息的獲取抽象爲一個單獨的服務, 這樣未來若是要實現從數據庫(或其它位置, 如網絡)獲取圖書信息, 只須要提供相關實現便可.
建立一個名爲"Services"的文件夾, 並建立IBookService
接口和BookService
實現類
IBookService
接口定義以下:
public interface IBookService { IEnumerable<Book> GetAllBooks(); }
如今只須要有一個方法: GetAllBooks
- 獲取全部圖書
BookService
類實現以下:
public class BookService : IBookService { private readonly List<Book> _bookStore; public BookService() { _bookStore = new List<Book> { new Book("阿米爾·汗:我行我素", BookType.Biography, DateTime.Parse("2017-6"), 52.8f, "https://img1.doubanio.com/view/subject/l/public/s29467958.jpg"), new Book("三體:「地球往事」三部曲之一", BookType.ScienceFiction, DateTime.Parse("2008-1"), 23f, "https://img1.doubanio.com/view/subject/l/public/s2768378.jpg"), new Book("三體Ⅱ:黑暗森林", BookType.ScienceFiction, DateTime.Parse("2008-5"), 32f, "https://img3.doubanio.com/view/subject/l/public/s3078482.jpg"), new Book("三體Ⅲ:死神永生", BookType.ScienceFiction, DateTime.Parse("2010-11"), 32f, "https://img9.doubanio.com/view/subject/l/public/s26012674.jpg"), new Book("肖申克的救贖", BookType.Mystery, DateTime.Parse("2006-7"), 26.9f, "https://img9.doubanio.com/view/subject/l/public/s4007145.jpg"), }; } public IEnumerable<Book> GetAllBooks() { return _bookStore; } }
在Bootstrapper
類中的ConfigureIoC
方法中, 註冊服務:
protected override void ConfigureIoC(IStyletIoCBuilder builder) { // Configure the IoC container in here builder.Bind<IBookService>().To<BookService>(); }
這樣咱們就能夠將IBookService
注入到須要使用的類中了.
MVVM中, 咱們可將界面拆解成一個個的小組件, 而後將它們組合在一塊兒造成一個複雜的界面. 這樣的好處有不少:
總之, 就是將UI的部分進行解耦, 達到分而治之的目的.
在未接觸MVMM以前, 也許我會將圖書顯示的UI代碼直接放在IndexView
中, 而學習了MVVM以後, 咱們就會很天然的想到將每一個圖書項目的顯示作成一個組件, 而後在IndexView
中將全部圖書組合成一個列表來顯示.
因此, 接下來看一下是如何建立圖書項目的.
在"Pages\Books"文件夾下, 建立"BookItems"文件夾, 並建立一個BookItemViewModel
:
public class BookItemViewModel : Screen { public Book Book { get; } public BookItemViewModel(Book book) { Book = book; } }
在同一文件夾內, 建立BookItemView
:
<UserControl ... d:DataContext="{d:DesignInstance bookItems:BookItemViewModel}" > <materialDesign:Card Background="WhiteSmoke"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Image Margin="0 10 0 0" Source="{Binding Book.CoverUrl}" Height="150" Stretch="Uniform" /> <DockPanel Grid.Row="1"> <TextBlock Margin="0 10 0 0" DockPanel.Dock="Top" FontWeight="Bold" Text="{Binding Book.Name}" HorizontalAlignment="Center"></TextBlock> <TextBlock Text="{Binding Book.Price, StringFormat='¥{0}'}" Margin="0 20 10 10" Foreground="Red" FontWeight="Bold" VerticalAlignment="Bottom" HorizontalAlignment="Right"></TextBlock> </DockPanel> </Grid> </materialDesign:Card> </UserControl>
BookItemViewModel
爲設計時實例, 爲XAML提供智能提示實際開發中, 可充分利用第一章中講解的Hot Reload功能, 在運行時調整XAML.
完成後的工程結構是這樣的:
有了圖書項目組件, 咱們就能夠來填充圖書列表了.
咱們使用ListView來顯示圖書信息, WPF中的ListView是一個很是靈活的控件, 配合WPF強大的模板特性, 在展示集合數據時, 幾乎能夠實現任何效果.
改造IndexViewModel
以下:
public class IndexViewModel : Screen { private readonly IBookService _bookService; public ObservableCollection<BookItemViewModel> BookItems { get; set; } = new ObservableCollection<BookItemViewModel>(); public IndexViewModel(IBookService bookService) { _bookService = bookService; } protected override void OnViewLoaded() { var viewModels = _bookService.GetAllBooks() .Select(book => new BookItemViewModel(book)) ; BookItems = new ObservableCollection<BookItemViewModel>(viewModels); } }
ObservableCollection
類型的屬性, 名爲BookItems
. 使用ObservableCollection能夠在圖書增長或減小時自動發送通知IBookService
, 並存儲爲成員變量OnViewLoaded
方法中, 調用IBookService.GetAllBooks
而後轉換成圖書列表ViewModel改造IndexView
以下:
<UserControl ... > <ListView ItemsSource="{Binding BookItems}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"> <ListView.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ListView.ItemsPanel> <ListView.ItemTemplate> <DataTemplate> <ContentControl s:View.Model="{Binding}"></ContentControl> </DataTemplate> </ListView.ItemTemplate> </ListView> </UserControl>
IndexViewModel
中BookItems
ContentControl
作爲ListView.ItemTemplate的數據模板
ShellView
中的寫法相似, 使用Stylet提供的s:View.Model
爲ContentControl綁定一個ViewModel(這裏便是BookItemViewModel), Stylet會自動爲該ContentControl加載View(即BookItemView)能夠看到, IndexView並不知道BookItemView的存在, 一切都是由後面的ViewModel關聯在一塊兒的, 這樣咱們就實現了View之間的解耦.
最後運行程序, 確認運行正常.
本章的任務就完成了. 在本章中, 咱們建立了一個圖書組件, 並使用ViewModel First來驅動各個UI部分.下一章中咱們會講解Master-Detail這種經典的數據表現形式. 但願朋友們能多多留言, 交流心得. 源碼託管在GITHUB上.
Happy Coding~