是否可以为可观察的属性覆盖参数关键字或创建新关键字?

Is it possible to override the Parameter keyword, or create a new Keyword, for observable properties?

在 VB.NET(不引用第 3 方库)中,我发现我正在编写很多以下代码:

Private _prop as String = "test"
Public Property Prop As String
  Get
    Return _prop
  End Get
  Set(value As String)
    _prop = value
    NotifyPropertyChanged()
  End Set

'(add more properties here...)

Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub NotifyPropertyChanged(<CallerMemberName> Optional propertyName As String = "")
    If PropertyChangedEvent IsNot Nothing Then
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End If
End Sub

我希望能够做的是:

Public ObservableProperty Prop As String = "test"

并让 ObservableProperty 成为我定义的构造,它完成所有样板文件的工作。

我知道如果我不想让它成为 Observable,我可以使用相同的模式 (Public Property NonObsProp As String = "test"),那么是否也可以让它们自动成为 observable?

受 Knockout.js 启发,减少 INPC 属性 样板的一种可能方法是定义可观察值类型。

Imports System.ComponentModel

Public Class ObservableValue(Of T)
    Implements INotifyPropertyChanging, INotifyPropertyChanged

    Public Event PropertyChanging As PropertyChangingEventHandler Implements INotifyPropertyChanging.PropertyChanging
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Private Shared ReadOnly __changing As PropertyChangingEventArgs = New PropertyChangingEventArgs("Value")
    Private Shared ReadOnly __changed As PropertyChangedEventArgs = New PropertyChangedEventArgs("Value")

    Public Sub New()
    End Sub

    Public Sub New(initialValue As T)
        _value = initialValue
    End Sub

    Private _value As T
    Public Property Value As T
        Get
            Return _value
        End Get
        Set(newValue As T)
            If Not EqualityComparer(Of T).Default.Equals(_value, newValue) Then
                RaiseValueChanging()
                _value = newValue
                RaiseValueChanged()
            End If
        End Set
    End Property

    Public Sub RaiseValueChanging()
        RaiseEvent PropertyChanging(Me, __changing)
    End Sub

    Public Sub RaiseValueChanged()
        RaiseEvent PropertyChanged(Me, __changed)
    End Sub

    Public Overrides Function ToString() As String
        Return If(_value IsNot Nothing, _value.ToString(), String.Empty)
    End Function

    Public Shared Widening Operator CType(value As T) As ObservableValue(Of T)
        Return New ObservableValue(Of T)(value)
    End Operator

End Class

这可以用于定义属性 - class 不一定需要实现 INotifyPropertyChanged,除非出于其他原因需要它。

Public Class MyViewModel
    Public ReadOnly Property Prop1 As ObservableValue(Of String) = "A"
    Public ReadOnly Property Prop2 As ObservableValue(Of Boolean) = False
End Class

然后你需要记住在 getting/setting 值时访问 .Value 属性(ReadOnly 有帮助,也许有更优雅的方式来公开潜在价值),例如在 WPF 绑定中:

vm.Prop1.Value = "NewValue"
<TextBlock Text="{Binding Prop1.Value}"/>

与典型的 INPC 模式相比,我还没有充分使用这种模式来发现它的缺点……甚至是优点。

@sstan 的评论为我指明了正确的方向。这不是一个完美的答案,但这是我目前能得到的最接近的答案。

本质上,这是参考 here 的方式,它建议创建一个调用 NotifyPropertyChangedSetProperty 方法,减少样板代码。

类似于:

protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }