對於客戶端應用程式而言,單頁應用程式(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上,點選此瞭解
歡迎關注我的公眾號: