0

Thanks to Marc Gravell's awesome answer I was able to implement property change tracking within my class. However, when the object is first initialized and the properties set for the first time all the properties are being marked as dirty. What would be the best way to handle the initial setting of the properties so that they are not marked dirty when first loading the object?

So when ExtendedFieldDto properties are first set dirtyProperties always has a count equal to the properties of the inheriting class, in this case ExtendedFieldDto.

public abstract class NotifyPropertyChanged {
    public IDictionary<string, object> dirtyProperties { get; private set; }

    protected NotifyPropertyChanged() {
        dirtyProperties = new Dictionary<string, object>();
    }

    protected void setProperty<T>(ref T property, T value, string propertyName) {
        if (!EqualityComparer<T>.Default.Equals(property, value)) {
            property = value;
            if (dirtyProperties.Keys.Contains(propertyName))
                dirtyProperties[propertyName] = property;
            else
                dirtyProperties.Add(propertyName, property);
        }
    }
}

public class ExtendedFieldDto : NotifyPropertyChanged {
    private string _id;
    public string id {
        get { return _id; }
        set { setProperty(ref _id, value, "id"); }
    }

    private int _idLocation;
    public int idLocation {
        get { return _idLocation; }
        set { setProperty(ref _idLocation, value, "idLocation"); }
    }

    private string _columnName;
    public string columnName {
        get { return _columnName; }
        set { setProperty(ref _columnName, value, "columnName"); }
    }

    private string _description;
    public string description {
        get { return _description; }
        set { setProperty(ref _description, value, "description"); }
    }

    private string _help;
    public string help {
        get { return _help; }
        set { setProperty(ref _help, value, "help"); }
    }
}

The dto is currently being used as a proprety on an aspx page where I'm storing the object in ViewState (a requirement from the legacy code). So I'm not sure how I would use the constructor for ExtendedFieldDto to set the private properties with this type of implementation.

private ExtendedFieldDto extendedField {
    get {
        if (ViewState[EXTENDED_FIELD_VIEWSTATE_KEY] == null)
            ViewState[EXTENDED_FIELD_VIEWSTATE_KEY] = new ExtendedFieldDto();
        return (ExtendedFieldDto)ViewState[EXTENDED_FIELD_VIEWSTATE_KEY];
    }
    set { ViewState[EXTENDED_FIELD_VIEWSTATE_KEY] = value; }
}
Community
  • 1
  • 1
bflemi3
  • 6,698
  • 20
  • 88
  • 155
  • Could you use a constructor which sets your private fields? This should not trigger setProperty. – Vlad Feb 05 '14 at 19:38
  • @Vlad Yes that would def work but the real object has over 20 properties and it makes management of the object (ie: adding/removing properties) kind of a pain. It might be the best way though :/ – bflemi3 Feb 05 '14 at 19:43
  • @Vlad Also, with my current implementation I am storing this object in ViewState so I have a property for the object in my aspx that, on the getter, initializes the object if the ViewState KeyValue Pair doesn't exist. So not sure how I would pass all the properties there. `get { if (ViewState[EXTENDED_FIELD_VIEWSTATE_KEY] == null) ViewState[EXTENDED_FIELD_VIEWSTATE_KEY] = new ExtendedFieldDto(); return (ExtendedFieldDto)ViewState[EXTENDED_FIELD_VIEWSTATE_KEY]; }` – bflemi3 Feb 05 '14 at 20:24

2 Answers2

5

This is one of a number of common problems for which the ISupportInitialize interface was created.

public abstract class NotifyPropertyChanged : ISupportInitialize {
   ...
   public void BeginInit() { }
   public void EndInit() { PropertyChangedObserver(); }
}

and you use it like this:

ExtendedFieldDto foo = new ExtendedFieldDto();
foo.BeginInit();
foo.id = "abc123";
foo.idLocation = 0;
...
foo.EndInit();

EDIT: You might also think about implementing INotifyPropertyChanged while you're at it, since you're already most of the way there.

Oblivious Sage
  • 3,326
  • 6
  • 37
  • 58
  • Nice interface: ISupportInitialize. I did not know it. Sadly, the XmlSerializer.Deserialize (.Net v4.0) does not seem to use it. – Knowleech Feb 06 '14 at 07:25
0

I had a very similar problem once. The idea of using a ctor did not work for me, because the object was created and initialized by XML deserialization.

The rather ugly solution I used was a pseudo-global initializing bool variable (thread-static variable, http://msdn.microsoft.com/library/system.threadstaticattribute%28v=vs.110%29.aspx). Before deserializing the data I set that bool to true and after deserialization completed I reset it to false (using a try-finally construct to be safe). In my PropertyChanged handlers I checked that bool. If it was true, I just did not fire my events.

That is a very ugly solution. I even don't like calling it a solution at all, because of the check of that bool variable every time the PropertyChanged handler is called. It is not a clean or save approach, as you must be sure to only set that bool during initialization and you must be sure to reset it afterwards. But it worked for me.

Knowleech
  • 1,013
  • 2
  • 9
  • 15
  • I've thought about this too but didn't like the idea of the client having to worry about anything but setting the object. – bflemi3 Feb 05 '14 at 20:21
  • Perhaps that can be modified to where the properties have some obscure initial values, and the setproperty isn't triggered if the value is changing 'from' the initial value. Though that would probably look pretty weird, and I'm not sure what the initial values can/should be. – Vlad Feb 05 '14 at 21:11