Wrapping the property setter
Asked Answered
P

2

8

I find that I'm repeating myself alot and that is of course no good. So I wondered if I could do something about it. This is a common code in my WPF application:

private string _name;
public string Name
{
    get { return _name; }
    set
    {
        if (_name != value)
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }
}

So I was wondering if I could wrap the setter somehow to make it better and more readable. One idea was something like this:

protected void PropertySetter<T>(T property, T value, string name)
{
    if (EqualityComparer<T>.Default.Equals(property, value))
    {
        property = value;
        OnPropertyChanged(name);
    }
}

Usage like this:

private string _name2;
public string Name2
{
    get { return _name2; }
    set
    {
        PropertySetter<string>(Name2, value, "Name2");
    }
}

But I'm not sure this is really smart or would work as well with Value types?

I guess I'm not the first one to try something like this so if someone knows a good foolproof way to something like this please chime in. I guess I couldn't make the propertyChanged typesafe without reflection but any ideas there would also help.

Propane answered 18/6, 2012 at 15:57 Comment(2)
Check ReactiveUI for this behaviour, if you don't want to use it then you might be able to copy it. reactiveui.net e.g. set { this.RaiseAndSetIfChanged(x => x.PasswordConfirmation, value); }Thibaut
Add ref to your property parameter and it will work for ValueTypes as well.Engstrom
H
2

Yes - this is completely acceptable and normal code.

Here's an example I found that's pretty standardized (I see a lot of this type of usage in code samples).

public event PropertyChangedEventHandler PropertyChanged;

private void SetProperty<T>(ref T field, T value, string name)
{
    if (!EqualityComparer<T>.Default.Equals(field, value))
    {
        field = value;
        var handler = PropertyChanged;
        if (handler != null)
        {
          handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

Wrap this code inside of a class that implements INotifyPropertyChanged, and inherit your data objects from this class.

In your example, you are calling the event directly - never do this. You could lose the event reference from the time the method starts to the time you call the event. Always create a local cache of the event before invoking it.

Hayashi answered 18/6, 2012 at 16:3 Comment(1)
Yes my OnPropertyChanged methods wraps the creating of a local copy handler. Thank you, missed the ref keyword.Nearsighted
B
2

Maybe this could help you

public class ObservableObject : INotifyPropertyChanged
{
    #region Events
    public event PropertyChangedEventHandler PropertyChanged;
    #endregion

    #region Protected Methods
    protected virtual void SetAndNotify<T>(ref T field, T value, Expression<Func<T>> property)
    {
        if (!object.ReferenceEquals(field, value))
        {
            field = value;
            this.OnPropertyChanged(property);
        }
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> changedProperty)
    {
        if (PropertyChanged != null)
        {
            string name = ((MemberExpression)changedProperty.Body).Member.Name;
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion
}

Usage:

private String _myField;
    public String MyProperty
    {
        get
        { return _myField; }
        set
        { SetAndNotify(ref _myField, value, () => MyProperty); }
    }

Edit: Your class must inherit from this OservableObject class

Batt answered 18/6, 2012 at 16:1 Comment(3)
Never call PropertyChanged directly - there are race conditions that will cause this to fail. Instead, always create a local copy and invoke that instead.Hayashi
Does the memberExpression reflection thing have any drawbacks, performancewise for example or situation they don't work?Nearsighted
@IngóVals I have implemented this class in two projects I have developed and there has been no problem so farBatt
H
2

Yes - this is completely acceptable and normal code.

Here's an example I found that's pretty standardized (I see a lot of this type of usage in code samples).

public event PropertyChangedEventHandler PropertyChanged;

private void SetProperty<T>(ref T field, T value, string name)
{
    if (!EqualityComparer<T>.Default.Equals(field, value))
    {
        field = value;
        var handler = PropertyChanged;
        if (handler != null)
        {
          handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

Wrap this code inside of a class that implements INotifyPropertyChanged, and inherit your data objects from this class.

In your example, you are calling the event directly - never do this. You could lose the event reference from the time the method starts to the time you call the event. Always create a local cache of the event before invoking it.

Hayashi answered 18/6, 2012 at 16:3 Comment(1)
Yes my OnPropertyChanged methods wraps the creating of a local copy handler. Thank you, missed the ref keyword.Nearsighted

© 2022 - 2024 — McMap. All rights reserved.