0

Is there an elegant way of setting up a property changed event that includes the data that changed when you don't know what type of data could be included in derived classes?

For example:

// I know I'm abusing the generics notation and this will not compile,
// but hopefully it will get my point across about what I'm trying to do.

class PropertyChangedEventArgs<T> : EventArgs
{
    public string PropertyName { get; private set; }
    public T Data { get; private set; }

    public PropertyChangedEventArgs(string name, T data)
    {
        PropertyName = name;
        Data = data;
    }
}

public class Person
{
    public event EventHandler<PropertyChangedEventArgs<T>> PropertyChanged = delegate { };

    public int Age {
        get;
        protected set {
            if (_age != value) {
                _age = value;
                OnPropertyChanged(new PropertyChangedEventArgs<int>("Age", _age));
            }
        }
    } private int _age;

    // More properties...

    protected void OnPropertyChanged(PropertyChangedEventArgs<T> eventArgs)
    {
        EventHandler<PropertyChangedEventArgs<T>> handler = PropertyChanged;
        if (handler != null) {
            handler (this, PropertyChangedEventArgs<T>);
        }
    }
}

// Later we might want something like this, but we want to use the same event handler

public class Athlete : Person
{   
    public CholesterolNumbers Cholesterol {
        get { return _cholesterol; }
        protected set {
            if (!_cholesterol.Equals(value)) {
                _cholesterol = value;

                // Same event, different data type:
                OnPropertyChanged(new PropertyChangedEventArgs<CholesterolNumbers>("Cholesterol", _cholesterol));
            }
        }
    } private CholesterolNumbers _cholesterol;

    public struct CholesterolNumbers {
        public int LDL;
        public int HDL;
        public int Trig;

        public CholesterolNumbers(int ldl, int hdl, int trig)
        {
            LDL = ldl;
            HDL = hdl;
            Trig = trig;
        }
    }
}

Is there a neat way of doing this so that any derived class can use the same EventHandler and we won't have to write an entirely new EventArgs class for each type we add later? I saw this solution to a very similar question, but their approach still had each event for each type named individually. I'm trying to avoid that.

Can this be done? Or should I rethink my approach?

Community
  • 1
  • 1
  • Use object as your type or mimic the generic eventhandler as inspiration. – kidshaw Mar 06 '16 at 18:18
  • Since `PropertyChanged` is raised for public properties, can't your client directly query the target object for the new value of that property, instead of passing it in the event? – dotNET Mar 06 '16 at 18:18

1 Answers1

0

Since PropertyChanged is raised for public properties, you can directly query the target object for the value of a property, instead of passing it in the event. For example, the Resize event (and Click and DoubleClick and so on) in Windows Forms doesn't carry Width and Height information with it. Instead you directly call Form.Width and Form.Height to get these values.

One scenario where this will not work is when you want to get both old and new values of the property, as the old value would already have been overwritten by the time your client gets notified about the change. PropertyChanging pattern provides an elegant solution of this problem. This event is raised by the class immediately before changing the property, thus allowing clients to read the old as well as new values before one of them is lost.

dotNET
  • 33,414
  • 24
  • 162
  • 251
  • You know what, I was so fixated on events passing the changed values that I completely forgot about the basics. I can indeed simply do this for most of my cases. I'm not going to mark this as the correct answer yet because it doesn't necessarily answer my question, but this is a good solution. Thank you for reminding me about common sense lol –  Mar 06 '16 at 18:29