本篇定位
ObservableRecipient 在 ObservableObject 之上增加了 IMessenger 消息收发能力。
1. 示例一:ViewModel 之间通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public sealed class UserSelectedMessage { public int UserId { get; } public UserSelectedMessage(int id) => UserId = id; }
public partial class UserListViewModel : ObservableRecipient { [ObservableProperty] private int _selectedUserId;
partial void OnSelectedUserIdChanged(int value) { Messenger.Send(new UserSelectedMessage(value)); } }
public partial class UserDetailViewModel : ObservableRecipient, IRecipient<UserSelectedMessage> { [ObservableProperty] private string _userName = "";
public UserDetailViewModel() { IsActive = true; }
public void Receive(UserSelectedMessage message) { LoadUser(message.UserId); }
private async void LoadUser(int id) { UserName = $"加载用户 {id}..."; await Task.Delay(500); UserName = $"用户 {id} - 张三"; } }
|
执行流程:
1 2 3 4 5
| 用户在 List 中点击 → SelectedUserId 变化 → OnSelectedUserIdChanged 触发 → Messenger.Send(UserSelectedMessage) → UserDetailViewModel.Receive 被调用 → LoadUser → UI 更新
|
2. 示例二:IsActive 生命周期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public partial class DocumentViewModel : ObservableRecipient { private bool _hasChanges; public bool HasChanges { get => _hasChanges; set => SetProperty(ref _hasChanges, value); }
public void OnDocumentOpened() { IsActive = true; }
public void OnDocumentClosed() { IsActive = false; }
protected override void OnActivated() { Messenger.Register<SaveRequestMessage>(this, (r, m) => { ((DocumentViewModel)r).Save(); });
}
private void Save() => HasChanges = false; }
|
3. 示例三:broadcast 参数(自动广播属性变化)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| public sealed class UserNameChangedMessage { public string OldName { get; } public string NewName { get; } public UserNameChangedMessage(string oldName, string newName) { OldName = oldName; NewName = newName; } }
public partial class MainViewModel : ObservableRecipient { private string _userName = "默认用户";
public string UserName { get => _userName; set => SetProperty(ref _userName, value, true); } }
public class StatusBarViewModel : IRecipient<PropertyChangedMessage<string>> { public StatusBarViewModel() { WeakReferenceMessenger.Default.RegisterAll(this); }
public void Receive(PropertyChangedMessage<string> message) { if (message.PropertyName == nameof(UserName)) { Console.WriteLine($"用户名: {message.OldValue} → {message.NewValue}"); } } }
|
4. IsActive 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| private bool isActive;
public bool IsActive { get => this.isActive; set { if (SetProperty(ref this.isActive, value, true)) { if (value) OnActivated(); else OnDeactivated(); } } }
protected virtual void OnActivated() { Messenger.RegisterAll(this); }
protected virtual void OnDeactivated() { Messenger.UnregisterAll(this); }
|
5. broadcast SetProperty 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| protected bool SetProperty<T>( [NotNullIfNotNull(nameof(newValue))] ref T field, T newValue, bool broadcast, [CallerMemberName] string? propertyName = null) { T oldValue = field; bool propertyChanged = SetProperty(ref field, newValue, propertyName);
if (propertyChanged && broadcast) Broadcast(oldValue, newValue, propertyName);
return propertyChanged; }
protected virtual void Broadcast<T>(T oldValue, T newValue, string? propertyName) { var message = new PropertyChangedMessage<T>(this, propertyName, oldValue, newValue); _ = Messenger.Send(message); }
|
6. 测试技巧:注入 Mock Messenger
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class TestViewModel : ObservableRecipient { public TestViewModel(IMessenger messenger) : base(messenger) { } }
[Test] public void Test_SendMessage() { var messenger = new WeakReferenceMessenger(); var vm = new TestViewModel(messenger); vm.IsActive = true;
}
|
7. 选择基类决策树
1 2 3 4 5 6
| 需要消息通信? ├─ 是 → ObservableRecipient │ └─ 需要广播属性变化?→ 用 SetProperty(..., broadcast: true) │ └─ 否 → ObservableObject └─ 需要数据验证?→ ObservableValidator
|
下一篇预告:ObservableValidator 源码分析 — 数据注解验证的实现