Unity應用架構設計(5)——ViewModel之間如何共享資料

木宛城主發表於2019-01-16

對於客戶端應用程式而言,單頁應用程式(Single Page Application)是最常見的表現形式。有經驗的開發人員往往會把一個View分解多個SubView。那麼,如何在多個SubView之間 『共享資料』 是一個很棘手的事情。又因為ViewModel才是真正為View提供資料來源,所以本質上『共享資料』指的是多個ViewModel之間共享同一塊資料控制元件。

JavaScript中的原型鏈

談到『共享』兩字,腦海裡跳出第一個印象就是『繼承』。對吧,因為你是父母的孩子,所以理所當然你可以和父母共享家中的一切。所以『共享』的前提,就是構建一個『繼承鏈』,也就是JavaScript中的『原型鏈』。

那麼JavaScript是怎樣實現原型鏈呢?有經驗的JavaScript程式設計師想必早就記的滾瓜爛熟了——通過內建屬性 __proto__ 來實現。

所以ViewModel之間『共享資料』的核心就是如何去實現一個繼承鏈,如下所示:

為ViewModel構建繼承關係

有了上述的分析之後,只要仿照JavaScript的 __proto__ 的實現,我們對所有ViewModel的基類ViewModelBase新增一個ParentViewModel 屬性,它代表當前ViewModel的父親物件。

public class ViewModelBase
{
    public ViewModelBase ParentViewModel { get; set; }
    //...
}複製程式碼

接著我參考了WPF中是怎樣獲取父ViewModel當中的資料:

 Binding="{Binding RelativeSource={RelativeSource FindAncestor, 
 AncestorType={x:Type Window}}, Path=DataContext.ParentViewModelProperty}複製程式碼

可以看到通過 FindAncestor 方法,去指定 AncestorType 型別的上層物件中獲取資料。

所以,我為ViewModelBase 增加一個擴充套件方法,可以通過繼承鏈實現從指定的祖先物件獲取資料。

public static IEnumerable<T> Ancestors<T>(this ViewModelBase origin) where T : ViewModelBase
{
    if (origin==null)
    {
        yield break;
    }
    var parentViewModel = origin.ParentViewModel;
    while (parentViewModel!=null)
    {
        var castedViewModel = parentViewModel as T;
        if (castedViewModel != null)
        {
            yield return castedViewModel;
        }
        parentViewModel = parentViewModel.ParentViewModel;
    }
}複製程式碼

對應在ViewModel中,可以通過 Ancestors擴充套件方法獲取上層物件的資料

var ancestors = this.Ancestors<FaceBoxViewModel>();複製程式碼

最後,以圖示的形式會更加直觀,下圖所示,SubViewModel依靠繼承鏈可以輕鬆訪問到ParentViewModel的共享資料:

小結

本篇文章介紹了怎樣在ViewModel之間共享資料,實際上解決方案是非常簡單的,人為的構造了一個繼承鏈並隨著繼承鏈往上找,總是能找到希望獲取到的資料。類似與JavaScript中的原型鏈,維護了一種至上而下的父子關係。
原始碼託管在Github上,點選此瞭解

歡迎關注我的公眾號:

相關文章