0

In VB.NET (without referencing 3rd-party libraries), I am finding that I am writing a lot of the following code:

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

What I would like to be able to do is something like:

Public ObservableProperty Prop As String = "test"

and have ObservableProperty be a construct I define that does all the boilerplate stuff.

I know that if I didn't want it to be Observable, I could use that same pattern (Public Property NonObsProp As String = "test"), so is it possible to make them observable automatically also?

simonalexander2005
  • 4,338
  • 4
  • 48
  • 92
  • Possible duplicate of [Simplest way to achieve automatic notification of property change](http://stackoverflow.com/questions/4716400/simplest-way-to-achieve-automatic-notification-of-property-change) – sstan Jul 27 '16 at 15:39
  • The answers to that question use 3rd party libraries, which I want to avoid – simonalexander2005 Jul 27 '16 at 15:41
  • 1
    Sadly, the answer to your question is no. You're in good company though. Many of us wish for a simpler and built-in solution. (Have a look here as well: http://stackoverflow.com/questions/3347309/inotifypropertychanged-and-auto-properties) – sstan Jul 27 '16 at 15:43
  • It was once proposed that properties be allowed to have `{Get, Set, Notify}` for such things, but MS declined. – Ňɏssa Pøngjǣrdenlarp Jul 27 '16 at 15:53
  • @sstan, the `SetProperty` idea referenced in the link you put (by @crea7or) is great! It removes the boilerplate which is really useful, and I think the closest I'm going to get to an answer. If you want to write it up I'll give you the answer - otherwise I'll do it myself. – simonalexander2005 Jul 28 '16 at 09:49

2 Answers2

1

One possible way to cut down on the INPC property boilerplate, inspired by Knockout.js, would be to define an observable value type.

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

This can then be used to define the properties - the class doesn't necessarily need to implement INotifyPropertyChanged, unless that is needed for some other reason.

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

Then you need to remember to access the .Value property when getting/setting the value (the ReadOnly helps, and perhaps there is a more elegant way to expose the underlying value), e.g. in WPF bindings:

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

I haven't used this pattern enough yet to discover the downsides... or even the upsides vs. the typical INPC patterns.

Mark
  • 8,140
  • 1
  • 14
  • 29
0

A comment from @sstan pointed me in the right direction. It's not a perfect answer, but it's the closest I'm going to get for now.

Essentially, it's the way referred to here, which suggests creating a SetProperty method which calls NotifyPropertyChanged, cutting down on the boilerplate code.

Something like:

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;
    }
simonalexander2005
  • 4,338
  • 4
  • 48
  • 92