- MVVM 教程
- MVVM - 首頁
- MVVM – 簡介
- MVVM - 優點
- MVVM - 職責
- MVVM - 第一個應用
- MVVM - 關聯檢視
- MVVM - 關聯 ViewModel
- MVVM - WPF 資料繫結
- MVVM - WPF 資料模板
- MVVM - ViewModel 通訊
- MVVM - 層次結構和導航
- MVVM - 驗證
- MVVM - 依賴注入
- MVVM - 事件
- MVVM - 單元測試
- MVVM - 框架
- MVVM - 面試問題
- MVVM 有用資源
- MVVM - 快速指南
- MVVM - 有用資源
- MVVM - 討論
MVVM – ViewModel 的關聯
在本章中,我們將介紹如何關聯 ViewModel。這是上一章的延續,在上一章中我們討論了 View 優先的構建。現在,第一個構建的下一種形式是一個稱為ViewModelLocator的元模式。它是一種偽模式,構建在 MVVM 模式之上。
在 MVVM 中,每個 View都需要與它的 ViewModel 關聯。
ViewModelLocator 是一種簡單的方法,可以集中程式碼並進一步解耦檢視。
這意味著它不必明確知道 ViewModel 型別以及如何構建它。
有多種不同的方法可以使用 ViewModelLocator,但這裡我們使用最類似於 PRISM 框架中的一部分的方法。
ViewModelLocator 提供了一種標準、一致、宣告式和松耦合的方式來進行 View 優先的構建,它自動化了將 ViewModel 關聯到 View 的過程。下圖表示了 ViewModelLocator 的高階流程。
步驟 1 - 確定正在構建的 View 型別。
步驟 2 - 識別該特定 View 型別的 ViewModel。
步驟 3 - 構建該 ViewModel。
步驟 4 - 將 View 的 DataContext 設定為 ViewModel。
為了理解基本概念,讓我們看看 ViewModelLocator 的簡單示例,繼續上一章中的相同示例。如果檢視 StudentView.xaml 檔案,您會看到我們已靜態地關聯了 ViewModel。
現在,如以下程式所示,註釋這些 XAML 程式碼,並從程式碼隱藏中刪除程式碼。
<UserControl x:Class = "MVVMDemo.Views.StudentView"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:local = "clr-namespace:MVVMDemo.Views"
xmlns:viewModel = "clr-namespace:MVVMDemo.ViewModel"
mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "300">
<!--<UserControl.DataContext>
<viewModel:StudentViewModel/>
</UserControl.DataContext>-->
<Grid>
<StackPanel HorizontalAlignment = "Left">
<ItemsControl ItemsSource = "{Binding Path = Students}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation = "Horizontal">
<TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}"
Width = "100" Margin = "3 5 3 5"/>
<TextBox Text = "{Binding Path = LastName, Mode = TwoWay}"
Width = "100" Margin = "0 5 3 5"/>
<TextBlock Text = "{Binding Path = FullName, Mode = OneWay}"
Margin = "0 5 3 5"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</UserControl>
現在讓我們建立一個新的資料夾 VML,並新增一個新的公共類 ViewModelLocator,它將包含一個附加屬性(依賴屬性)AutoHookedUpViewModel,如下面的程式碼所示。
public static bool GetAutoHookedUpViewModel(DependencyObject obj) {
return (bool)obj.GetValue(AutoHookedUpViewModelProperty);
}
public static void SetAutoHookedUpViewModel(DependencyObject obj, bool value) {
obj.SetValue(AutoHookedUpViewModelProperty, value);
}
// Using a DependencyProperty as the backing store for AutoHookedUpViewModel.
//This enables animation, styling, binding, etc...
public static readonly DependencyProperty AutoHookedUpViewModelProperty =
DependencyProperty.RegisterAttached("AutoHookedUpViewModel",
typeof(bool), typeof(ViewModelLocator), new PropertyMetadata(false,
AutoHookedUpViewModelChanged));
現在您可以看到一個基本的附加屬性定義。要向屬性新增行為,我們需要為此屬性新增一個更改事件處理程式,其中包含自動關聯 View 的 ViewModel 的過程。執行此操作的程式碼如下所示:
private static void AutoHookedUpViewModelChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) {
if (DesignerProperties.GetIsInDesignMode(d)) return;
var viewType = d.GetType();
string str = viewType.FullName;
str = str.Replace(".Views.", ".ViewModel.");
var viewTypeName = str;
var viewModelTypeName = viewTypeName + "Model";
var viewModelType = Type.GetType(viewModelTypeName);
var viewModel = Activator.CreateInstance(viewModelType);
((FrameworkElement)d).DataContext = viewModel;
}
以下是 ViewModelLocator 類的完整實現。
using System;
using System.ComponentModel;
using System.Windows;
namespace MVVMDemo.VML {
public static class ViewModelLocator {
public static bool GetAutoHookedUpViewModel(DependencyObject obj) {
return (bool)obj.GetValue(AutoHookedUpViewModelProperty);
}
public static void SetAutoHookedUpViewModel(DependencyObject obj, bool value) {
obj.SetValue(AutoHookedUpViewModelProperty, value);
}
// Using a DependencyProperty as the backing store for AutoHookedUpViewModel.
//This enables animation, styling, binding, etc...
public static readonly DependencyProperty AutoHookedUpViewModelProperty =
DependencyProperty.RegisterAttached("AutoHookedUpViewModel",
typeof(bool), typeof(ViewModelLocator), new
PropertyMetadata(false, AutoHookedUpViewModelChanged));
private static void AutoHookedUpViewModelChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e) {
if (DesignerProperties.GetIsInDesignMode(d)) return;
var viewType = d.GetType();
string str = viewType.FullName;
str = str.Replace(".Views.", ".ViewModel.");
var viewTypeName = str;
var viewModelTypeName = viewTypeName + "Model";
var viewModelType = Type.GetType(viewModelTypeName);
var viewModel = Activator.CreateInstance(viewModelType);
((FrameworkElement)d).DataContext = viewModel;
}
}
}
首先要做的是新增一個名稱空間,以便我們可以在專案的根目錄中訪問該 ViewModelLocator 型別。然後在路由元素(一種檢視型別)上,新增 AutoHookedUpViewModel 屬性並將其設定為 true。
xmlns:vml = "clr-namespace:MVVMDemo.VML" vml:ViewModelLocator.AutoHookedUpViewModel = "True"
以下是 StudentView.xaml 檔案的完整實現。
<UserControl x:Class = "MVVMDemo.Views.StudentView"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:local = "clr-namespace:MVVMDemo.Views"
xmlns:viewModel = "clr-namespace:MVVMDemo.ViewModel"
xmlns:vml = "clr-namespace:MVVMDemo.VML"
vml:ViewModelLocator.AutoHookedUpViewModel = "True"
mc:Ignorable = "d" d:DesignHeight = "300" d:DesignWidth = "300">
<!--<UserControl.DataContext>
<viewModel:StudentViewModel/>
</UserControl.DataContext>-->
<Grid>
<StackPanel HorizontalAlignment = "Left">
<ItemsControl ItemsSource = "{Binding Path = Students}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation = "Horizontal">
<TextBox Text = "{Binding Path = FirstName, Mode = TwoWay}"
Width = "100" Margin = "3 5 3 5"/>
<TextBox Text = "{Binding Path = LastName, Mode = TwoWay}"
Width = "100" Margin = "0 5 3 5"/>
<TextBlock Text = "{Binding Path = FullName, Mode = OneWay}"
Margin = "0 5 3 5"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</UserControl>
編譯並執行上述程式碼後,您將看到 ViewModelLocator 正在為該特定 View 關聯 ViewModel。
需要注意的關鍵一點是,檢視不再以某種方式耦合到其 ViewModel 的型別或構建方式。所有這些都已移至 ViewModelLocator 內部的中心位置。