Search code examples
c#wpfmvvminotifypropertychanged

Making the children of a class communicate witch eachtother


I have trouble with the ViewModel of my MVVM pattern-code.

I have a bunch of measurements and a bunch of rules to evaluate the measurements, stored in the classes Rule and Measurement. In my main class MyClass I store my Rules and Measurements then in ObservableCollections (OC) (and connected to a DataGrid).

For all n Rules I create n CollcetionOfEvaluators in one OC and pass the respective rule and all the measurements to each single one. In each CollectionOfEvaulators I create for the one rule and the m Measurements m Evaluators in an OC.

The Evaluators take the one Rule and the one Measurement and gives back a bool if or if not the respective Measurement passes the respective Rule.

I then have a ListView that displays for each Rule a DataGrid that shows for every Measurement if it passed the Rule.

My problem is to make the Evaluator class to fire the OnPropertyChanged method, if I change the properties of one of the measurements in MyClass. How can I pass the info basically from one child to another child's child? When I play around with the DataGrid of the Evaluators, for example click on the header to rearrange it, it works. So I guess the problem is the c# code not the xaml. So I will leave it out for once. All the bindings are Mode=TwoWay (except the bool, since it has no setter) and UpdateSourceTrigger=PropertyChanged.

I tried to sketch the problem: enter image description here

This is my code:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;

namespace demo
{
    public class MyClass : INotifyPropertyChanged
    {
        public class Measurement : INotifyPropertyChanged
        {
            private double? myValue1;

            public double? MyValue1
            {
                get { return myValue1; }
                set
                {
                    myValue1 = value;
                    OnPropertyChanged("MyValue1");
                }
            }

            private double? myValue2;

            public double? MyValue2
            {
                get { return myValue2; }
                set
                {
                    myValue2 = value;
                    OnPropertyChanged("MyValue2");
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;

            private void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }

        }

        public class EvaluationRule
        {
            public EvaluationRule(double Value1Min, double Value2Min)
            {
                this.Value1Min = Value1Min;
                this.Value2Min = Value2Min;
            }

            public double Value1Min;
            public double Value2Min;

        }

        public class Evaluator : INotifyPropertyChanged
        {
            public Evaluator(Measurement Measurement, EvaluationRule Rule)
            {
                this.Rule = Rule;
                this.Measurement = Measurement;
            }

            public EvaluationRule Rule;

            private Measurement measurement;
            public Measurement Measurement
            {
                get { return measurement; }
                set
                {
                    measurement = value;
                    OnPropertyChanged("Measurement");
                }
            }

            public bool IsApproved
            {
                get
                {
                    if (measurement.MyValue1 > Rule.Value1Min
                        && measurement.MyValue2 > Rule.Value2Min)
                    {
                        return true;
                    }
                    return false;
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;

            private void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public class CollectionOfEvaluators : INotifyPropertyChanged
        {
            public CollectionOfEvaluators(EvaluationRule Rule, ObservableCollection<Measurement> Measurements)
            {
                this.Rule = Rule;
                this.Measurements = Measurements;

                var Evaluators = new ObservableCollection<Evaluator>();
                foreach (var _measurement in Measurements)
                {
                    var _evaluator = new Evaluator(_measurement, this.Rule);
                    Evaluators.Add(_evaluator);
                }
            }

            public EvaluationRule Rule;

            private ObservableCollection<Measurement> measurements;

            public ObservableCollection<Measurement> Measurements
            {
                get { return measurements; }
                set
                {
                    measurements = value;
                    OnPropertyChanged("Measurements");
                }
            }

            private ObservableCollection<Evaluator> evaluators;

            public ObservableCollection<Evaluator> Evaluators
            {
                get { return evaluators; }
                set
                {
                    evaluators = value;
                    OnPropertyChanged("Evaluators");
                }
            }

            public event PropertyChangedEventHandler PropertyChanged;

            private void OnPropertyChanged(string propertyName)
            {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private ObservableCollection<Measurement> measurements;

        public ObservableCollection<Measurement> Measurements
        {
            get { return measurements; }
            set
            {
                measurements = value;
                OnPropertyChanged("Measurements");
            }
        }

        private ObservableCollection<EvaluationRule> rules;

        public ObservableCollection<EvaluationRule> Rules
        {
            get { return rules; }
            set
            {
                rules = value;
                GetCollection();
            }
        }

        private ObservableCollection<CollectionOfEvaluators> collection;

        public ObservableCollection<CollectionOfEvaluators> Collection
        {
            get { return collection; }
            set
            {
                collection = value;
                OnPropertyChanged("Collection");
            }
        }

        public void GetCollection()
        {
            var Collection = new ObservableCollection<CollectionOfEvaluators>();
            foreach (var _rule in rules)
            {
                var _collection = new CollectionOfEvaluators(_rule, Measurements);
                Collection.Add(_collection);
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Solution

  • You must delegate the event. Evaluator should listen to the PropertyChanged event of its aggregated Measurement. The handler of this event can then raise the Evaluator.PropertyChanged event in response:

    public class Evaluator : INotifyPropertyChanged
    {
      public Evaluator(Measurement measurement, EvaluationRule rule)
      {
        this.Rule = rule;
        this.Measurement = measurement;
    
        this.Measurement.PropertyChanged += OnMeasurementPropertyChanged;
      }
    
      public void OnMeasurementPropertyChanged(object sender, PropertyChangedEventAgrs e)
      {
        OnPropertyChanged(nameof(this.Measurement));
      }
    
      private Measurement measurement;
      public Measurement Measurement
      {
        get  => this.measurement
        set
        {
          this.measurement = value;
          OnPropertyChanged(nameof(this.Measurement));
        }
      }
    
      public event PropertyChangedEventHandler PropertyChanged;
      private void OnPropertyChanged(string propertyName)
      {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
    }
    

    Note that you got a spelling error when naming your class. It's Measurement - you missed an 'a'. Also parameter names should always be lowercase.