How to let a parent class know about a change in its children?

后端 未结 5 1175
走了就别回头了
走了就别回头了 2021-02-09 00:28

This is an example code:

public class MyParent : INotifyPropertyChanged
{
    List MyChildren;

    public bool IsChanged
    {
        get
               


        
5条回答
  •  野性不改
    2021-02-09 01:15

    Doing this kind of communication can be tricky, especially if you want to avoid memory leaks due to the event handlers that you hook up. There is also the case of handling items that are added / removed from the collection.

    I've really enjoyed the power and simplicity of the Continuous LINQ project on codeplex. It has some very rich features for setting up "Reactive Objects", "Continuous Values", and "Continuous Collections". These let you define your criteria as a Linq expression and then let the CLINQ library keep the underlying values up to date in real time.

    In your case, you could set up the parent with a ContinuousFirstOrDefault() linq query that watched for any child where "IsChanged == true". As soon as a child sets the value to true and raises PropertyChanged, the continuous value will detect the change and raise a corresponding PropertyChanged in the parent.

    The benefits:

    1. Weak references and weak events are used to prevent the event handlers in the parent from locking the child in memory. It can get very messy to add / remove these handlers from all the children.
    2. You can declare the dependency in the parent without need to make special changes in the child or make the child aware of the parent. Rather, the child just needs to properly implement INotifyPropertyChanged. This puts the "logic" close to the object that cares, rather than spreading event craziness and inter-dependencies all over the code.

    Here's what the code might look like:

    public class MyParent : INotifyPropertyChanged
    {
    
        private ObservableCollection _MyChildren;
        private ContinuousValue _ContinuousIsChanged = null;
    
        public MyParent()
        {
            _MyChildren = new ObservableCollection();
    
            // Creat the ContinuousFirstOrDefault to watch the MyChildren collection.
            // This will monitor for newly added instances, 
            // as well as changes to the "IsChanged" property on 
            // instances already in the collection.
            _ContinuousIsChanged = MyChildren.ContinuousFirstOrDefault(child => child.IsChanged);
            _ContinuousIsChanged.PropertyChanged += (s, e) => RaiseChanged("IsChanged");
        }
    
        public ObservableCollection MyChildren
        {
            get { return _MyChildren; }
        }
    
        public bool IsChanged
        {
            get
            {
                // If there is at least one child that matches the 
                // above expression, then something has changed.
                if (_ContinuousIsChanged.Value != null)
                    return true;
    
                return false;
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void RaiseChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }
    
    public class MyChild : INotifyPropertyChanged
    {
        private int _Value;
        public int Value
        {
            get
            {
                return _Value;
            }
            set
            {
                if (_Value == value)
                    return;
                _Value = value;
                RaiseChanged("Value");
                RaiseChanged("IsChanged");
            }
        }
        private int _DefaultValue;
        public int DefaultValue
        {
            get
            {
                return _DefaultValue;
            }
            set
            {
                if (_DefaultValue == value)
                    return;
                _DefaultValue = value;
                RaiseChanged("DefaultValue");
                RaiseChanged("IsChanged");
            }
        }
    
        public bool IsChanged
        {
            get
            {
                return (Value != DefaultValue);
            }
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void RaiseChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propName));
        }
    }
    

    The above code sets up the ContinuousFirstOrDefault in the constructor so that it is always monitoring. However, in some cases you can optimize this by lazily instantiating the ContinuousFirstOrDefault only when the getter for "IsChanged" is called. That way you don't start monitoring for changes until you know that some other piece of code actually cares.

提交回复
热议问题