1. 程式人生 > >【我們一起寫框架】MVVM的WPF框架之綁定(二)

【我們一起寫框架】MVVM的WPF框架之綁定(二)

static blog ica navig 創建 string 一是 user 業務

MVVM的特點之一是實現數據同步,即,前臺頁面修改了數據,後臺的數據會同步更新。

上一篇我們已經一起編寫了框架的基礎結構,並且實現了ViewModel反向控制Xaml窗體。

那麽現在就要開始實現數據同步了。

DataContext—數據上下文

在實現數據同步前,我們要了解一個知識點——DataContext。

WPF中每個UI都有一個Content和一個DataContext,那麽Content和DataContext是什麽呢?

Content:Content是指頁面內容,即我們編寫的代碼,或者認為它是展示的UI。

打個比方,Content就是HTML頁面中的標簽,如【<html></html】;那麽,在WPF中Content是指的就是Xaml頁面的標簽了。

DataContext:DataContext是指頁面中的數據內容,這部分內容只有運行了才存在,用過ASP.NET MVC的同學可以把它理解為MVC中的Model。(每個頁面都有一個唯一的指定Model)

既然在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中,默認的綁定是單向綁定,就是說,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上了,並且會持續更新。

相關文章:

【我們一起寫框架】MVVM的WPF框架之序篇(一)

To be continued

Github地址:https://github.com/kiba518/KibaFramework

----------------------------------------------------------------------------------------------------

註:此文章為原創,歡迎轉載,請在文章頁面明顯位置給出此文鏈接!
若您覺得這篇文章還不錯,請點擊下右下角的推薦】,非常感謝!
如果您覺得這篇文章對您有所幫助,那就不妨支付寶小小打賞一下吧。

技術分享圖片

【我們一起寫框架】MVVM的WPF框架之綁定(二)