views:

465

answers:

3

hELLO !

Here i have a simple class example with three fields of type class B and some other stuff. As you can see im listening on every child object change. Since i could need alot of properties of type class B i wonder if there is a way of shrinking the code. Creating a listener + a method for each seems like i will have ALOT of code. How would i fix this ... using a dictionary or something similar? I have been told that IoC could fix this, but im not sure where to start.

public class A : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public int _id;
    public int Id
    {
        get { return _id; }
        set
        {
            if (_id == value)
            {
                return;
            }

            _id = value;
            OnPropertyChanged("Id"); 
        }
    }

    public string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
            {
                return;
            }

            _name = value; 
            OnPropertyChanged("Name"); 
        }
    }

    public B _firstB;
    public B FirstB
    {
        get { return _firstB; }
        set 
        {
            if (_firstB == value)
            {
                return;
            }

            if (_firstB != null)
            {
                FirstB.PropertyChanged -= firstObjectB_Listener;
            }

            _firstB = value;

            if (_firstB != null) 
                FirstB.PropertyChanged += new PropertyChangedEventHandler(firstObjectB_Listener);

            OnPropertyChanged("FirstB"); 
        }
    }

    public B _secondB;
    public B SecondB
    {
        get { return _secondB; }
        set
        {
            if (_secondB == value)
            {
                return;
            }

            if (_secondB != null)
            {
                FirstB.PropertyChanged -= secondObjectB_Listener;
            }

            _secondB = value;

            if (_secondB != null)
                SecondB.PropertyChanged += new PropertyChangedEventHandler(secondObjectB_Listener);

            OnPropertyChanged("FirstB");
        }
    }

    public B _thirdB;
    public B ThirdB
    {
        get { return _thirdB; }
        set
        {
            if (_thirdB == value)
            {
                return;
            }

            if (_thirdB != null)
            {
                ThirdB.PropertyChanged -= thirdObjectB_Listener;
            }

            _thirdB = value;

            if (_thirdB != null)
                ThirdB.PropertyChanged += new PropertyChangedEventHandler(thirdObjectB_Listener);

            OnPropertyChanged("ThirdB");
        }
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    void firstObjectB_Listener(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Object A has found a change of " + e.PropertyName + " on first object B");
    }

    void secondObjectB_Listener(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Object A has found a change of " + e.PropertyName + " on second object B");
    }

    void thirdObjectB_Listener(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Object A has found a change of " + e.PropertyName + " on third object B");
    }
}
A: 

One nice way to simplify the setup of your properties can be found here.

Regarding to your cascading notifications: I'd guess that you could use the approach outlined above to handle the (un-)subscription of events for properties implementing INotifyPropertyChanged there.

Benjamin Podszun
A: 

To simplify at little bit you can do the following two things.

First, in the handler of the PropertyChanged the first parameter, sender, is the object that fired the event, at least if you have implmented the OnPropertyChanged in class B the same way as in class A. This means you only need one handler for all the B properties.

private void BValueListener(object sender, PropertyChangedEventArgs e)
{
  Console.WriteLine("Found change of {0} on object {1}", e.PropertyName, sender);
}

If you need to do know exactly which of the B properties did the sending you could do checks in the BValueListener method.

if (sender == FirstB) { /* Do special stuff here */ }

Having the same listener for all the B properties we can then move on to write the property setter like:

private B _thirdB;
public B ThirdB
{
  get { return _thirdB; }
  set {
    if (UpdateBValue(ref _thirdB, value)) {
      OnPropertyChanged("ThirdB");
    }
  }
}
private bool UpdateBValue(ref B value, B newValue)
{
  if (value == newValue)
  {
    return false;
  }

  if (value != null)
  {
    value.PropertyChanged -= BValueListener;
  }

  value = newValue;
  if (value != null)
  {
    value.PropertyChanged += BValueListener;
  }
  return true;
}

If you really need different handlers for each property you can modify the code above to something like

private B _thirdB;
public B ThirdB
{
    get { return _thirdB; }
    set 
    { 
        if (UpdateBValue(ref _thirdB, value, BValueListener))
        {
            OnPropertyChanged("ThirdB");
        }
    }
} 

private bool UpdateBValue(ref B value, B newValue, PropertyChangedEventHandler eventHandler)
{
    if (value == newValue)
    {
        return false;
    }

    if (value != null)
    {
        value.PropertyChanged -= eventHandler;
    }

    value = newValue;

    if (value != null)
    {
        value.PropertyChanged += eventHandler;
    }
    return true;
}

where you can send in the listener method you would like to use in each case.

Robert Höglund
thanx for the anwser Robert. But i need to go a step further.I need to create some sort of "helper" class that would handle events for my classes. My goal is to keep the classes clean as much as possible (perhaps even the INotifyPropertyChanged interface injected on demand). I have been reading about "TypeDescriptor" can do this, but to be honest im still in the dark...
no9
A: 

The most elegant way I know of is to use Aspect Oriented Programming (AOP) with a tool such as PostSharp. I found INotifyPropertyChanged implementation examples here and here. These allow you to decorate your properties with an attribute and PostSharp then implements INotifyPropertyChanged for you when the code is built.

Jamie Ide
I have been looking into PostSharp alot. The problem is that i cannot get child property change chain to work in it. Maybe there is some solution for PostSharp 2.0, but we are using PostSharp 1.5 and the solutions do not work there. I can pack INotifyPropertyChange as an aspect allrite, but i cant figure out how to handle child changes (any level). Any idea?
no9
I haven't actually used it. They do have a support forum (http://www.sharpcrafters.com/forum/) and I noticed that 1.5 is marked as "legacy" so you'll probably have to upgrade to get it working.
Jamie Ide