目錄
1. MvvmLight 框架準備
2. MvvmLight 中的相關基類
3. MvvmLight 中的數據綁定與通知
a. 核心功能
b. 關鍵方法與屬性
c. 完整示例
d. 高級用法
4. MvvmLight 中的命令對象
a. 命令對象的作用
b. 核心接口:ICommand
c. MvvmLight 中的 RelayCommand
d. 動態更新命令的可執行狀態
e. 高級用法
5. Messenger 對象使用
a. Messenger 的核心作用
b. MvvmLight 中的 Messenger 實現
c. 基本使用場景
d. 高級用法
6. DispatcherHelper 對象使用
7. SimpleIoc 對象使用
a. SimpleIoc 的核心作用
b. 核心方法與屬性
c. 基本使用步驟
d. 高級用法
e. 與 ViewModelLocator 結合使用
1. MvvmLight 框架準備
- 作用: 快速搭建 MVVM 架構的應用程序,簡化數據綁定、命令和消息傳遞。
- 步驟: 通過 NuGet 安裝
MvvmLightLibs
包。
2. MvvmLight 中的相關基類
- 核心基類:
-
ViewModelBase
: ViewModel 基類,實現INotifyPropertyChanged
。
public class MainViewModel : ViewModelBase
{private string _name;public string Name{get => _name;set => Set(ref _name, value); // 自動觸發 PropertyChanged}
}
-
ObservableObject
: 輕量級可觀察對象。RelayCommand
: 命令對象基類。
3. MvvmLight 中的數據綁定與通知
a. 核心功能
ViewModelBase
繼承自 ObservableObject
,并實現了 INotifyPropertyChanged
接口,主要負責:
- 屬性變更通知:當 ViewModel 的某個屬性值發生變化時,自動通知 UI 更新。
- 簡化代碼:通過
Set
方法簡化屬性定義,避免手動觸發PropertyChanged
事件。 - 設計模式支持:提供靜態屬性
IsInDesignMode
,用于區分代碼是在設計時(如 Visual Studio 設計器)還是運行時執行。
b. 關鍵方法與屬性
(1) Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
- 作用:在屬性的
set
方法中調用此方法,自動比較新舊值,若不同則更新字段并觸發PropertyChanged
事件。 - 示例:
private string _name;
public string Name
{get => _name;set => Set(ref _name, value); // 自動觸發通知
}
(2)RaisePropertyChanged(string propertyName)
- 作用:手動觸發某個屬性的
PropertyChanged
事件。 - 場景:當某個屬性的值依賴于其他屬性時,手動通知 UI 更新。
public string FullName => $"{FirstName} {LastName}";private string _firstName;
public string FirstName
{get => _firstName;set{Set(ref _firstName, value);RaisePropertyChanged(nameof(FullName)); // 通知 FullName 屬性變化}
}
(3) IsInDesignMode
- 作用:靜態屬性,判斷當前代碼是否在設計器(如 Visual Studio 或 Blend)中運行。
- 用途:在設計時提供假數據,避免調用真實服務或數據庫。
public class MainViewModel : ViewModelBase
{public MainViewModel(){if (IsInDesignMode)Name = "Design Mode Sample"; // 設計器顯示假數據elseLoadRealData(); // 運行時加載真實數據}
}
c. 完整示例
using GalaSoft.MvvmLight;public class UserViewModel : ViewModelBase
{private string _userName;private int _age;public string UserName{get => _userName;set => Set(ref _userName, value);}public int Age{get => _age;set => Set(ref _age, value);}// 計算屬性(依賴其他屬性)public string UserInfo => $"{UserName} (Age: {Age})";// 當 UserName 或 Age 變化時,手動通知 UserInfo 更新protected override void OnPropertyChanged(string propertyName = null){base.OnPropertyChanged(propertyName);if (propertyName == nameof(UserName) || propertyName == nameof(Age))RaisePropertyChanged(nameof(UserInfo));}
}
d. 高級用法
(1)批量通知多個屬性
通過 RaisePropertyChanged(null)
或指定空字符串,通知所有屬性更新(慎用,可能影響性能):
public void ResetAllProperties()
{_userName = "Default";_age = 0;RaisePropertyChanged(""); // 通知所有屬性更新
}
(2) 繼承與擴展
可繼承 ViewModelBase
并添加通用邏輯(如日志記錄、驗證):
public abstract class CustomViewModelBase : ViewModelBase
{protected void LogPropertyChange(string propertyName){Debug.WriteLine($"屬性 {propertyName} 已更新");}public override void RaisePropertyChanged(string propertyName = null){base.RaisePropertyChanged(propertyName);LogPropertyChange(propertyName);}
}
4. MvvmLight 中的命令對象
a. 命令對象的作用
- 解耦 UI 與業務邏輯:將用戶操作(如點擊按鈕)映射到 ViewModel 的方法。
- 控制可執行狀態:根據條件動態啟用或禁用 UI 元素(例如按鈕的
IsEnabled
)。 - 支持參數傳遞:允許從 UI 傳遞參數到 ViewModel(如選中項的 ID)。
b. 核心接口:ICommand
所有命令對象均實現 System.Windows.Input.ICommand
接口,其定義如下:
public interface ICommand
{event EventHandler CanExecuteChanged; // 通知命令可執行狀態變化bool CanExecute(object parameter); // 判斷命令是否可執行void Execute(object parameter); // 執行命令邏輯
}
c. MvvmLight 中的 RelayCommand
MvvmLight 提供 RelayCommand
和 RelayCommand<T>
類,簡化了 ICommand
的實現。
(1) 基本用法(無參數)
public class MainViewModel : ViewModelBase
{public RelayCommand SaveCommand { get; }public MainViewModel(){// 初始化命令:綁定方法 + 可執行條件SaveCommand = new RelayCommand(SaveData, CanSave);}private void SaveData(){// 保存邏輯}private bool CanSave(){return !string.IsNullOrEmpty(Name); // 僅當 Name 非空時按鈕可用}
}
(2) 支持參數傳遞(RelayCommand<T>)
public RelayCommand<string> FilterCommand { get; }public MainViewModel()
{FilterCommand = new RelayCommand<string>(param => ApplyFilter(param), param => !string.IsNullOrEmpty(param));
}private void ApplyFilter(string keyword)
{// 根據關鍵字過濾數據
}
d. 動態更新命令的可執行狀態
(1) 手動觸發更新
當 CanExecute
依賴的屬性變化時,調用 RelayCommand
的 RaiseCanExecuteChanged
方法:
private string _name;
public string Name
{get => _name;set{Set(ref _name, value);SaveCommand.RaiseCanExecuteChanged(); // 觸發重新檢查 CanSave}
}
(1) 自動觸發更新
利用 ViewModelBase
的 Set
方法自動觸發屬性變更通知,無需手動調用 RaiseCanExecuteChanged
:
private string _name;
public string Name
{get => _name;set => Set(ref _name, value); // Set 方法已自動觸發 PropertyChanged
}// CanSave 方法中依賴 Name 屬性
private bool CanSave() => !string.IsNullOrEmpty(Name);
e. 高級用法
(1) 異步命令
直接在命令中執行異步操作時,需注意線程安全(通過 DispatcherHelper
):
public RelayCommand LoadDataCommand { get; }public MainViewModel()
{LoadDataCommand = new RelayCommand(async () => await LoadDataAsync());
}private async Task LoadDataAsync()
{try{IsLoading = true;var data = await _dataService.GetData();// 更新 UI(確保在 UI 線程)DispatcherHelper.CheckBeginInvokeOnUI(() => DataList = data);}finally{IsLoading = false;}
}
(2) 復合命令
將多個命令組合成一個邏輯操作:
public RelayCommand SubmitAllCommand { get; }public MainViewModel()
{SubmitAllCommand = new RelayCommand(() =>{SaveCommand.Execute(null);LogCommand.Execute(null);}, () => SaveCommand.CanExecute(null) && LogCommand.CanExecute(null));
}
(3) 命令的泛型約束
public RelayCommand<int> DeleteItemCommand { get; }public MainViewModel()
{DeleteItemCommand = new RelayCommand<int>(id => DeleteItem(id), id => id > 0);
}private void DeleteItem(int itemId)
{// 刪除指定 ID 的項
}
5. Messenger 對象使用
在 MVVM 模式 中,Messenger 是一個用于實現 松耦合通信 的核心組件,尤其在 MvvmLight 框架 中被廣泛使用。它允許不同組件(如 ViewModel、View、服務)之間通過消息進行通信,而無需直接引用彼此,從而降低依賴、提升代碼可維護性。
a. Messenger 的核心作用
- 解耦組件通信:組件無需持有對方引用,通過消息訂閱/發布機制交互。
- 跨層級傳遞數據:例如從子 ViewModel 通知父 ViewModel,或跨頁面傳遞狀態。
- 支持復雜場景:如廣播通知、請求-響應模式、事件聚合等。
b. MvvmLight 中的 Messenger 實現
MvvmLight 的 Messenger
類是一個靜態單例(Messenger.Default
),提供消息的注冊、發送和注銷功能。其核心方法如下:
方法 | 作用 |
| 訂閱類型為 的消息。 為接收者標識(通常為 )。 |
| 發送一條 類型的消息,所有訂閱者會收到通知。 |
| 注銷某個接收者的所有消息訂閱,避免內存泄漏。 |
c. 基本使用場景
(1) 發送簡單通知消息
- 場景:ViewModel A 完成數據加載后,通知 ViewModel B 刷新界面。
發送消息方:
// 發送一個無參數通知
Messenger.Default.Send(new NotificationMessage("DataLoaded"));
接收消息方(ViewModel B):
public ViewModelB()
{// 注冊接收 NotificationMessage 類型的消息Messenger.Default.Register<NotificationMessage>(this, message =>{if (message.Notification == "DataLoaded"){RefreshData();}});
}
(2) 傳遞數據對象
- 場景:用戶選擇某條數據后,跨頁面傳遞選中項。
定義消息類型:
public class ItemSelectedMessage
{public int ItemId { get; set; }public ItemSelectedMessage(int id) => ItemId = id;
}
發送方:
// 用戶選擇某項后發送消息
Messenger.Default.Send(new ItemSelectedMessage(selectedItem.Id));
接收方:
Messenger.Default.Register<ItemSelectedMessage>(this, message =>
{LoadItemDetails(message.ItemId); // 根據 ItemId 加載詳情
});
d. 高級用法
(1) 消息令牌(Token)
- 作用:區分相同消息類型的不同用途,避免消息沖突。
發送帶令牌的消息:
// 發送消息時指定令牌
Messenger.Default.Send(new NotificationMessage("UpdateChart"), "ChartToken" // 令牌標識
);
接收指定令牌的消息:
Messenger.Default.Register<NotificationMessage>(this, message => UpdateChartData(), "ChartToken" // 僅接收帶有此令牌的消息
);
(2) 泛型消息
- 場景:傳遞強類型數據,避免類型轉換。
定義泛型消息:
public class GenericMessage<T>
{public T Content { get; }public GenericMessage(T content) => Content = content;
}
發送泛型消息:
var data = new List<User>();
Messenger.Default.Send(new GenericMessage<List<User>>(data));
接收泛型消息:
Messenger.Default.Register<GenericMessage<List<User>>>(this, message =>
{UserList = message.Content; // 直接獲取 List<User>
});
(3) 雙向通信(請求-響應模式)
- 場景:ViewModel A 請求數據,ViewModel B 響應返回結果。
發送請求消息:
public class DataRequestMessage
{public Action<string> Callback { get; set; } // 定義回調委托
}// 發送請求,并注冊回調
Messenger.Default.Send(new DataRequestMessage
{ Callback = response => HandleResponse(response)
});
接收請求并響應:
Messenger.Default.Register<DataRequestMessage>(this, message =>
{var data = FetchDataFromService(); // 獲取數據message.Callback?.Invoke(data); // 執行回調
});
6. DispatcherHelper 對象使用
- 作用: 在非 UI 線程更新 UI。
- 初始化:
DispatcherHelper.Initialize(); // 在 App.xaml.cs 中調用
- 使用:
Task.Run(() =>{// 后臺線程操作DispatcherHelper.CheckBeginInvokeOnUI(() =>{// 更新 UI 元素StatusText = "Processing...";});});
7. SimpleIoc 對象使用
SimpleIoc
是 MvvmLight 框架中提供的一個輕量級依賴注入(Dependency Injection, DI)容器,用于管理應用程序中各個組件(如 ViewModel、服務、數據源)的依賴關系。它通過 控制反轉(IoC) 和 依賴注入 機制,幫助開發者實現代碼解耦、提高可測試性和可維護性。
a. SimpleIoc 的核心作用
- 解耦組件依賴:將類的依賴關系從代碼中抽離,通過容器統一管理。
- 單例生命周期管理:默認以單例模式提供實例,避免重復創建對象。
- 簡化實例獲取:通過接口或類型直接獲取已注冊的實例。
- 支持構造函數注入:自動解析構造函數參數,完成依賴注入。
b. 核心方法與屬性
以下是 SimpleIoc
的常用方法及其功能:
方法/屬性 | 作用 |
| 注冊接口 和其實現類 。 |
| 直接注冊類型 (無接口)。 |
| 獲取類型 的實例(已注冊的接口或類)。 |
| 檢查類型 的實例是否已被創建。 |
| 重置容器,清空所有注冊和實例(用于測試或重新初始化)。 |
| 檢查類型 是否已注冊。 |
c. 基本使用步驟
(1) 注冊服務與 ViewModel
在應用程序啟動時(如 ViewModelLocator
或 App.xaml.cs
中),注冊所有依賴項:
public class ViewModelLocator
{public ViewModelLocator(){// 注冊服務(接口 + 實現類)SimpleIoc.Default.Register<IDataService, DataService>();// 注冊 ViewModel(無接口)SimpleIoc.Default.Register<MainViewModel>();}// 提供 ViewModel 實例的公共屬性public MainViewModel Main => SimpleIoc.Default.GetInstance<MainViewModel>();
}
(2) 獲取實例
通過 GetInstance<T>
方法獲取已注冊的實例:
// 獲取 ViewModel 實例
var mainVM = SimpleIoc.Default.GetInstance<MainViewModel>();// 獲取服務實例
var dataService = SimpleIoc.Default.GetInstance<IDataService>();
(3) 構造函數注入
當注冊的類(如 ViewModel)依賴其他服務時,SimpleIoc
會自動解析構造函數參數:
public class MainViewModel : ViewModelBase
{private readonly IDataService _dataService;// 構造函數依賴注入:自動傳入已注冊的 IDataService 實例public MainViewModel(IDataService dataService){_dataService = dataService;}
}
d. 高級用法
(1) 單例模式 vs. 瞬時模式
單例模式(默認):整個應用程序生命周期內只創建一個實例。
// 默認單例模式
SimpleIoc.Default.Register<IDataService, DataService>();
瞬時模式:每次調用 GetInstance
時創建新實例。
SimpleIoc.Default.Register<IDataService, DataService>(createInstanceImmediately: false);
var service = SimpleIoc.Default.GetInstance<IDataService>(); // 每次返回新實例
(2) 手動指定實例
允許直接注冊一個已存在的對象實例:
var logger = new FileLogger();
SimpleIoc.Default.Register<ILogger>(() => logger); // 注冊現有實例
(3) 依賴覆蓋
在測試中,可以替換實現類以注入 Mock 對象:
// 生產環境注冊真實服務
SimpleIoc.Default.Register<IDataService, DataService>();// 測試環境覆蓋為 Mock 服務
SimpleIoc.Default.Unregister<IDataService>();
SimpleIoc.Default.Register<IDataService, MockDataService>();
e. 與 ViewModelLocator 結合使用
ViewModelLocator
是 MvvmLight 中用于集中管理 ViewModel 的類,通常與 SimpleIoc
配合使用,通過 XAML 綁定 ViewModel。
(1) 定義 ViewModelLocator
public class ViewModelLocator
{public ViewModelLocator(){// 注冊服務和 ViewModelSimpleIoc.Default.Register<IDataService, DataService>();SimpleIoc.Default.Register<MainViewModel>();}// 暴露 ViewModel 屬性供 XAML 綁定public MainViewModel Main => SimpleIoc.Default.GetInstance<MainViewModel>();
}
(2) 在 XAML 中聲明資源
在 App.xaml
中合并 ViewModelLocator
:
<Application.Resources><ResourceDictionary><vm:ViewModelLocator x:Key="Locator" /></ResourceDictionary>
</Application.Resources>
(3) 在 View 中綁定 ViewModel
<Window DataContext="{Binding Main, Source={StaticResource Locator}}"><!-- UI 元素綁定到 MainViewModel 的屬性 -->
</Window>