MVVM的特色之一是實現數據同步,即,前臺頁面修改了數據,後臺的數據會同步更新。html
上一篇咱們已經一塊兒編寫了框架的基礎結構,而且實現了ViewModel反向控制Xaml窗體。git
那麼如今就要開始實現數據同步了。github
DataContext—數據上下文框架
在實現數據同步前,咱們要了解一個知識點——DataContext。函數
WPF中每一個UI都有一個Content和一個DataContext,那麼Content和DataContext是什麼呢?this
Content:Content是指頁面內容,即咱們編寫的代碼,或者認爲它是展現的UI。spa
打個比方,Content就是HTML頁面中的標籤,如【<html></html】;那麼,在WPF中Content是指的就是Xaml頁面的標籤了。htm
DataContext:DataContext是指頁面中的數據內容,這部份內容只有運行了才存在,用過ASP.NET MVC的同窗能夠把它理解爲MVC中的Model。(每一個頁面都有一個惟一的指定Model)blog
既然在WPF裏DataContext就是MVC中的Model。那麼,天然的,DataContext就要存儲頁面的ViewModel了,因此,咱們爲它賦值它自身對應的ViewModel。繼承
如今,找到咱們的BaseViewModel的構造函數,加入這行代碼[UIElement.DataContext = this;],代碼以下:
public BaseViewModel()
{
WindowMain = Application.Current.MainWindow;
SetUIElement();
UIElement.DataContext = this;
}
這樣用ViewModel建立的頁面的DataContext就被自動賦值了。
頁面與ViewModel的基礎關係就創建完成了。
Binding—綁定
在咱們編寫的框架中,綁定分兩種,一種是屬性綁定,一種是命令綁定。
屬性綁定:屬性綁定很好理解,就是將Xaml頁面的控件屬性和ViewModel中的自定義屬性捆綁到一塊兒,讓他們的數據值同步。
命令綁定:命令綁定是Xaml頁面觸發命令,而後由ViewModel來處理命令。
這裏的命令(Command)有點不太好理解,不過你們都作過面向事件的開發,咱們能夠把命令想象成事件,就是Xaml頁面觸發事件,ViewModel來執行事件內容。
接下來,咱們一塊兒作一些簡單的綁定。
Property—屬性綁定
首先,在程序框架中找到VM_WindowMain頁面,而後在裏面建立屬性HeaderName,代碼以下:
public string _HeaderName = "HeaderName_KibaFramework"; public string HeaderName { get { return _HeaderName; } set { _HeaderName = value; OnPropertyChanged(); } }
而後,咱們再找到VM對應的Xam頁面—WindowMain.xaml,修改Header代碼以下:
<StackPanel DockPanel.Dock="Top" Background="Gainsboro">
<TextBlock TextAlignment="Left" Text="{Binding HeaderName}" Margin="20,20,0,0" Height="70" FontSize="36"></TextBlock>
</StackPanel>
界面效果以下:
經過圖片,咱們能夠看到,屬性已經綁定成功了,而且成功輸出了咱們的HeaderName。
而後,咱們重點看一下這段代碼{Binding HeaderName}。
這句話的意思就是讓TextBlock的Text屬性綁定HeaderName屬性,其中Binding就是綁定的意思。【注意,這裏只能是屬性綁定屬性】
HeaderName是咱們在VM中剛剛定義的屬性,那麼Text是怎麼綁定到了HeaderName上的呢?
很簡單,由於上面咱們已經把ViewModel賦值到了DataContext中了,因此在Xaml中,咱們就可使用{Binding 屬性名}這樣的語句,來綁定VM中全部的屬性。
在Xaml中,TextBlock默認的綁定是單向綁定,就是說,VM中的屬性值改變會同步Xaml頁面的屬性值,讓其改變;但,當Xaml頁面的屬性值改變了,VM中的屬性值卻不會改變。
那麼如何讓他們同步呢?
很簡單,只須要在綁定的時候多加一個屬性Mode=TwoWay便可,代碼以下:
{Binding HeaderName,Mode=TwoWay}
Command—命令綁定
在MVVM中,事件被極大的程度的弱化了,由於Command在ViewModel中替代了事件來處理業務邏輯,因此,事件在框架中就只負責處理UI變化這麼一件事了。
BaseCommand
在WPF中,系統爲咱們提供一些Command,但爲了能處理更多細節,自定義Command的效果會更好,因此,咱們須要編寫屬於咱們框架本身的自定義BaseCommand。
代碼以下:
public class BaseCommand : ICommand { public Action<object> ExecuteAction; public BaseCommand(Action<object> action) { ExecuteAction = action; } public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { ExecuteAction(parameter); } }
如上代碼所示,咱們自定義了BaseCommand,而且繼承了ICommand接口,實現了接口方法。
Command的應用
下面咱們開始Command的基礎應用,使用Command實現頁面切換;頁面切換咱們採用最簡單的模式Window—Frame—Page的控制模式。
首先咱們找到VM_WindowMain,建立切換Page的Command和存儲頁面實例的屬性FrameSource。
代碼以下:
public Page _FrameSource; public Page FrameSource { get { return _FrameSource; } set { _FrameSource = value; OnPropertyChanged(); } } public BaseCommand ChangeFrameSourceCommand { get { return new BaseCommand(ChangeFrameSourceCommand_Executed); } } public void ChangeFrameSourceCommand_Executed(object obj) { string pageName = obj.ToString(); switch(pageName) { case "PageMain": FrameSource = new VM_PageMain().UIElement as Page; break; case "PageUser": FrameSource = new VM_PageUser().UIElement as Page; break; } }
接下來在頁面實現按鈕事件綁定和Frame顯示頁面綁定。
代碼以下:
<TreeViewItem> <TreeViewItem.Template> <ControlTemplate> <Button HorizontalAlignment="Left" Content="PageMain" Command="{Binding ChangeFrameSourceCommand}" CommandParameter="PageMain" Style="{StaticResource NullButton}"></Button> </ControlTemplate> </TreeViewItem.Template> </TreeViewItem> <TreeViewItem> <TreeViewItem.Template> <ControlTemplate> <Button HorizontalAlignment="Left" Content="PageUser" Command="{Binding ChangeFrameSourceCommand}" CommandParameter="PageUser" Style="{StaticResource NullButton}"></Button> </ControlTemplate> </TreeViewItem.Template> </TreeViewItem> /* 省略了框架其餘元素代碼 */ <Frame x:Name="frameMain" Content="{Binding FrameSource,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" NavigationUIVisibility="Hidden" ScrollViewer.CanContentScroll="True" ></Frame>
從代碼中咱們能夠看到,VM中的屬性FrameSource綁定到了頁面Frame的Content屬性上。
因爲TreeViewItem沒有Command的依賴屬性,因此咱們修改了他的模板,而後用模板內的Button的Command屬性綁定了VM中的ChangeFrameSourceCommand屬性。
由於ChangeFrameSourceCommand是BaseCommand類型,因此,當按鈕被按下時,就會觸發ChangeFrameSourceCommand定義的執行命令——ChangeFrameSourceCommand_Executed。
這樣咱們就實現了框架內的頁面切換了。
----------------------------------------------------------------------------------------------------
到此,咱們框架的基礎功能就已經實現了。
但若是框架只寫到這裏,那ViewModel對頁面的掌控力度就顯的太弱了。
並且項目框架不能僅僅考慮結構分離和業務獨立,咱們還要下降使用難度和提升使用者的開發效率。
因此爲了更好的掌控UI,下降開發者的門檻,咱們還須要編寫數據控件,讓開發者在不能熟練掌握Xaml樣式的狀況下,依然能夠順利完成開發。
那麼,本篇文章就先講到這了,下一篇文章咱們將一塊兒爲框架編寫數據控件,敬請期待。
框架代碼已經傳到Github上了,而且會持續更新。
相關文章:
To be continued
Github地址:https://github.com/kiba518/KibaFramework
----------------------------------------------------------------------------------------------------
注:此文章爲原創,任何形式的轉載都請聯繫做者得到受權並註明出處!
若您以爲這篇文章還不錯,請點擊下方的【推薦】,很是感謝!