2

In C#, I have a suffiently complex Model. I already have a WPF Client to manipulate that model. I'm using MVVM. All objects in that model support INotifyPropertyChanged and all properties that are collections support INotifyCollectionChanged.

Take this as a simplied example:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;


namespace CollectionTest1
{
    public class PropertyChangedSupport : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void FirePropertyChange([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public class Company : PropertyChangedSupport
    {
        private string name;
        public String Name { get { return name; } set { name = value; FirePropertyChange(); } }
        public ObservableCollection<Employee> Employees { get; } = new ObservableCollection<Employee>();

    }

    public class Employee : PropertyChangedSupport
    {
        private string name;
        public String Name { get { return name; } set { name = value; FirePropertyChange(); } }
        public ObservableCollection<PresentTimespan> PresentTimespans { get; } = new ObservableCollection<PresentTimespan>();

        public Boolean IsPresentAt(DateTime t)
        {
            foreach (PresentTimespan pt in PresentTimespans)
            {
                if (pt.Start.CompareTo(t) <= 0 && pt.Finish.CompareTo(t) >= 0) return true;
            }
            return false;
        }
    }

    public class PresentTimespan : PropertyChangedSupport
    {
        private string comment;
        public String Comment { get { return comment; } set { comment = value; FirePropertyChange(); } }
        private DateTime start;
        public DateTime Start { get { return start; } set { start = value; FirePropertyChange(); } }

        private DateTime finish;
        public DateTime Finish { get { return finish; } set { finish = value; FirePropertyChange(); } }
    }

    public class CompanyStatusView : PropertyChangedSupport
    {
        private DateTime currentTime;
        public DateTime CurrentTime { get { return currentTime; } set { currentTime = value; FirePropertyChange(); } }

        private Company currentCompany;
        public Company CurrentCompany { get { return currentCompany; } set { currentCompany = value; FirePropertyChange(); } }

        public ObservableCollection<Employee> PresentEmployees { get; } = new ObservableCollection<Employee>();

        public CompanyStatusView()
        {
            UpdatePresentEmployees();

        }

        private void UpdatePresentEmployees()
        {
            PresentEmployees.Clear();
            foreach (Employee e in CurrentCompany.Employees) {
                if (e.IsPresentAt(currentTime)) PresentEmployees.Add(e);
            }
        }
    }
}

I'd like to have UpdatePresentEmployees called whenever there are changes in:

  • Collection Company.Employees.PresentTimespans
  • Property Company.Employees.PresentTimespans.Start
  • Property Company.Employees.PresentTimespans.Finish
  • Collection Company.Employees
  • Property CurrentTime
  • Property CurrentCompany

So it's basically any property or collection read by UpdatePresentEmployees.

My best solution so far included registering a lot of event handlers to all the objects mentioned above. That included to have a couple of Dictionary instances to track which added objects I have to subscribe to and especially which I have to unsubscribe from.

The most difficult and annoying part was to subscribe to all the PresentTimespan objects to listen for property changes and all the PresentTimespans collections of Employee to listen for collection changes.

My guess is that there has to be a better way to do this.

After all, in JFace (Java) there is a very interesting solution that uses ObservableTracker. So there you'd only provide the code for UpdatePresentEmployees and ObservableTracker tracks which objects have been read and automatically makes you listen for changes in any of these and also correctly unsubscribes from irrelevant objects. So there are better approaches to this problem in general. What is C# offering? Can it do better than my best solution I mentioned above? Can I avoid some of the boilerplate code? Can it be done with .net provided classes or do I need some additional classes/libraries?

Thanks for your kind help and advice in advance!

mm8
  • 163,881
  • 10
  • 57
  • 88

2 Answers2

0

You could use BindingList instead of ObservableCollection and attach to the the ListChanged Event. But keep in mind that BindingList has some disadvantages like not being very fast. For further information this could be interesting: difference between ObservableCollection and BindingList

If you dont wanna use BindingList you have to wire your items with events.

Community
  • 1
  • 1
Mighty Badaboom
  • 6,067
  • 5
  • 34
  • 51
  • The model I mentioned in my question is stripped down. Actually Company, Employee would be complemented by many many other classes and have more properties. So if I used BindingList I would always get all notifications, right? I cannot subscribe just to those mentioned above, right? I'm not sure, so please correct be if I'm wrong. – Bernd Fuhrmann Mar 03 '17 at 09:45
  • That's correct you would get a notification if any of the properties changed. You could check in the ListChanged method which property was changed but the notification still will be fired for each change. If you don#t want this you could make own events in your children and attach to them in the collections. But this can end in a mess of code :/ – Mighty Badaboom Mar 03 '17 at 09:48
  • I don't think that this would be feasible in the long run, but thanks for bringing this approach up. It could have been a workaround, but alas it's not a clean solution. – Bernd Fuhrmann Mar 03 '17 at 09:56
-1

As pointed out by Nikhil Agrawal, Rx or ReactiveUI is a good framework for my purpose. So I consider that to be a solution.