You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Having followed the recommendation to replace all field-based observable properties with partial properties, I've encountered an annoying behavioural change. Namely, it is no longer possible to initialize the property value in a (non-primary) constructor without triggering the whole "property changed" process.
This is particularly problematic if the view-model tried to notify a parent view-model when the property changes, since the notification fires before the parent view-model's field/property has been set.
For example, using fields, this works:
class Child : ObservableObject
{
private readonly Action _onChanged;
[ObservableProperty]
private int _value;
public Child(Action onChanged, int value)
{
_onChanged = onChanged;
_value = value;
}
partial void OnValueChanged(int newValue)
{
_onChanged();
}
}
class Parent
{
public Child Foo { get; }
public Parent(int value)
{
Foo = new(value, OnChildChanged);
}
private void OnChildChanged()
{
MessageBox.Show($"Child changed: {Child.Value}");
}
}
After changing the child class to use a partial property:
class Child : ObservableObject
{
private readonly Action _onChanged;
[ObservableProperty]
public partial int Value { get; set; }
public Child(Action onChanged, int value)
{
_onChanged = onChanged;
Value = value;
}
partial void OnValueChanged(int newValue)
{
_onChanged();
}
}
the Parent constructor now immediately throws a NullReferenceException. The onChanged callback is called before the Child constructor returns, meaning that the Parent.Foo property has not been assigned yet.
One workaround is to change the Child class to use a primary constructor:
class Child(Action onChanged, int value) : ObservableObject
{
private readonly Action _onChanged = onChanged;
[ObservableProperty]
public partial int Value { get; set; } = value;
partial void OnValueChanged(int newValue)
{
_onChanged();
}
}
The property initializer sets the backing field directly, rather than calling the set method. But that's not always possible.
Another option would be to store a "constructor completed" flag, and check that in every "changed" callback:
class Child : ObservableObject
{
private readonly Action _onChanged;
private readonly bool _construcorCompleted;
[ObservableProperty]
public partial int Value { get; set; }
public Child(Action onChanged, int value)
{
_onChanged = onChanged;
Value = value;
_construcorCompleted = true;
}
partial void OnValueChanged(int newValue)
{
if (!_construcorCompleted) return;
_onChanged();
}
}
But that increases the size of the class, bloats the code, and is easy to forget to check.
The finaly option I came up with - assuming the callback is the only issue - is to not set the callback field until the constructor completes. Either leave it as null, and check in every "changed" event; or set it to a no-op action.
static class NoOp
{
public static readonly Action Instance = delegate { };
}
class Child : ObservableObject
{
private readonly Action _onChanged;
[ObservableProperty]
public partial int Value { get; set; }
public Child(Action onChanged, int value)
{
_onChanged = NoOp.Instance;
Value = value;
_onChanged = onChanged;
}
partial void OnValueChanged(int newValue)
{
_onChanged();
}
}
Are there any other options for initializing an observable partial property in the constructor without triggering the "property changed" process? At least until we get something like the fieldof operator.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
Having followed the recommendation to replace all field-based observable properties with partial properties, I've encountered an annoying behavioural change. Namely, it is no longer possible to initialize the property value in a (non-primary) constructor without triggering the whole "property changed" process.
This is particularly problematic if the view-model tried to notify a parent view-model when the property changes, since the notification fires before the parent view-model's field/property has been set.
For example, using fields, this works:
After changing the child class to use a partial property:
the
Parentconstructor now immediately throws aNullReferenceException. TheonChangedcallback is called before theChildconstructor returns, meaning that theParent.Fooproperty has not been assigned yet.One workaround is to change the
Childclass to use a primary constructor:The property initializer sets the backing field directly, rather than calling the
setmethod. But that's not always possible.Another option would be to store a "constructor completed" flag, and check that in every "changed" callback:
But that increases the size of the class, bloats the code, and is easy to forget to check.
The finaly option I came up with - assuming the callback is the only issue - is to not set the callback field until the constructor completes. Either leave it as
null, and check in every "changed" event; or set it to a no-op action.Are there any other options for initializing an observable partial property in the constructor without triggering the "property changed" process? At least until we get something like the
fieldofoperator.Beta Was this translation helpful? Give feedback.
All reactions