让我解释一下 `PropertyChanged` 事件的订阅过程。在 WPF 中,当我们使用数据绑定时,WPF 框架会自动订阅 `PropertyChanged` 事件。让我们看看完整的流程:
1. **事件声明**:
```csharp
public event PropertyChangedEventHandler? PropertyChanged;
```
2. **事件触发**:
在您的 `MainViewModel` 中,当属性值改变时,通过 `OnPropertyChanged` 方法触发事件:
```csharp
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
```
3. **实际订阅发生在 XAML 绑定时**:
```xml
<Window.DataContext>
<viewmodels:MainViewModel/>
</Window.DataContext>
<TextBlock Text="{Binding Message}"
FontSize="24"
Margin="0,0,0,20"/>
```
当 XAML 中使用 `Binding` 时,WPF 框架会:
1. 创建一个 `BindingExpression` 对象
2. 这个对象会自动订阅目标对象(ViewModel)的 `PropertyChanged` 事件
3. 当事件触发时,更新 UI 元素
让我们通过一个具体例子来说明:
```csharp
public class MainViewModel : INotifyPropertyChanged
{
private string _message = "Hello World";
// 属性
public string Message
{
get => _message;
set
{
if (_message != value)
{
_message = value;
// 当值改变时,触发PropertyChanged事件
OnPropertyChanged(nameof(Message));
}
}
}
// 事件声明
public event PropertyChangedEventHandler? PropertyChanged;
// 触发事件的方法
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
```
当这个 ViewModel 被设置为 Window 的 DataContext 时:
```xml
<TextBlock Text="{Binding Message}"/>
```
WPF 内部会执行类似这样的操作(这是简化的示意,实际实现更复杂):
```csharp
// WPF框架内部的绑定处理(简化版)
var viewModel = (MainViewModel)DataContext;
viewModel.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "Message")
{
// 更新UI元素的值
textBlock.Text = viewModel.Message;
}
};
```
所以,虽然在代码中我们没有看到显式的订阅,但是:
1. WPF 的绑定系统会自动处理订阅
2. 每个绑定到 ViewModel 属性的 UI 元素都会创建一个订阅
3. 当我们调用 `OnPropertyChanged` 时,所有订阅的 UI 元素都会收到通知并更新
这就是为什么我们只需要:
1. 实现 `INotifyPropertyChanged` 接口
2. 声明 `PropertyChanged` 事件
3. 在属性变化时调用 `OnPropertyChanged`
WPF 框架会处理剩下的所有事情,包括事件订阅和 UI 更新。这是 WPF 数据绑定系统的一个重要特性,它使得 MVVM 模式的实现变得更加简单和优雅。