Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to propagate property change notifications of objects within collections

Lets say I have classes like this

public class R
{
    protected string name;
    protected List<S> listOfObjectS;
}

public class S
{
    private string name, ID;
    private A objectA;
}

public class A
{
    private string name;
    private int count;
}

If a user has two views open, one displaying instances of R and another allowing users to modify an instance of A, I need the view of R to change when the user changes any instance of A.

If the user changes a property of an instance of A, what is the best way to propagate that change (through instances of S) so that all instances of R display the new state of A?

like image 308
Error Avatar asked Dec 30 '13 19:12

Error


People also ask

How will an object be notified if the property bound to it has been changed?

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed. For example, consider a Person object with a property called FirstName .

How do you implement property change?

To implement INotifyPropertyChanged you need to declare the PropertyChanged event and create the OnPropertyChanged method. Then for each property you want change notifications for, you call OnPropertyChanged whenever the property is updated.

What does RaisePropertyChanged do?

RaisePropertyChanged Method (ScheduleListDataSource)Notifies the data source of a change to a property value of the specified dataObject.

What is OnPropertyChanged C#?

INotifyPropertyChanged is an interface member in System. ComponentModel Namespace. This interface is used to notify the Control that property value has changed. Sourcecode.zip.


1 Answers

EDIT: Overhauling this answer to be more specific to the question since the tags show you already knew about INotifyPropertyChanged.

You need to implement INotifyPropertyChanged in class A and in class S. Make it so objectA can only be set through a property that will raise the PropertyChanged event on S whenever a property is changed in A. Example:

public class A : INotifyPropertyChanged
{
    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; OnPropertyChanged("Name"); }
    }

    private int count;

    public int Count
    {
        get { return count; } 
        set { count = value; OnPropertyChanged("Count"); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

... and class S...

public class S : INotifyPropertyChanged
{
    private string name, ID;
    private A objectA;

    public A ObjectA
    {
        get { return objectA; }
        set
        {
            var old = objectA;
            objectA = value;

            // Remove the event subscription from the old instance.
            if (old != null) old.PropertyChanged -= objectA_PropertyChanged;

            // Add the event subscription to the new instance.
            if (objectA != null) objectA.PropertyChanged += objectA_PropertyChanged;

            OnPropertyChanged("ObjectA");
        }
    }

    void objectA_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        // Propagate the change to any listeners. Prefix with ObjectA so listeners can tell the difference.
        OnPropertyChanged("ObjectA." + e.PropertyName);
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

For class R, use ObservableCollection<S> instead of List<S>, and subscribe to its CollectionChanged event, and monitor when objects are added or removed to listOfObjectS. When they are added, subscribe to S's PropertyChanged events. Then updated R's view. Example:

public class R
{
    protected string name;
    protected System.Collections.ObjectModel.ObservableCollection<S> ListOfObjectS { get; private set; }

    public R()
    {
        // Use ObservableCollection instead.
        ListOfObjectS = new ObservableCollection<S>();

        // Subscribe to all changes to the collection.
        ListOfObjectS.CollectionChanged += listOfObjectS_CollectionChanged;
    }

    void listOfObjectS_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            // When items are removed, unsubscribe from property change notifications.
            var oldItems = (e.OldItems ?? new INotifyPropertyChanged[0]).OfType<INotifyPropertyChanged>();
            foreach (var item in oldItems)
                item.PropertyChanged -= item_PropertyChanged;
        }

        // When item(s) are added, subscribe to property notifications.
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            var newItems = (e.NewItems ?? new INotifyPropertyChanged[0]).OfType<INotifyPropertyChanged>();
            foreach (var item in newItems)
                item.PropertyChanged += item_PropertyChanged;
        }

        // NOTE: I'm not handling NotifyCollectionChangedAction.Reset.
        // You'll want to look into when this event is raised and handle it
        // in a special fashion.
    }

    void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName.StartsWith("ObjectA."))
        {
            // Refresh any dependent views, forms, controls, whatever...
        }
    }
}
like image 194
David Schwartz Avatar answered Oct 04 '22 19:10

David Schwartz