views:

284

answers:

1

This problem could be bad class design or ignorance - please bear with me:

I have 2 classes - Chip (which implements INotifyPropertyChanged and represents a single poker chip) and ChipSet, which implements INotifyPropertyChanged and has an ObservableCollection of Chip.

I have a Datagrid which is bound to the Chip ObservableCollection and a Textblock which is bound to ChipSet.

ie. gridChips.ItemsSource = chipset.Chips;

The Chip class has 3 properties (for simplicity) - NumberPerPlayer, ChipValue and TotalValuePerPlayer. TotalValuePerPlayer does not have a property set or associated private member variable like the other 2 - it is dynamically based off the product of ChipValue and NumberPerPlayer.

The grid binds to all 3 of these values and shows these 3 columns. Only the first 2 are editable and the 3rd one updates as the other 2 changes.

This works fine so far - I found that in order to get the TotalValuePerPlayer column to update if either of the other columns updated I had to add this field to PropertyChangedEventArgs (see code below).

My first question- Is this the best way of updating bound class fields that are based off other fields and do not change in the UI (you cannot edit TotalValuePerPlayer directly).

public int NumberPerPlayer
{
    get { return numberPerPlayer; }
    set
    {
        if (numberPerPlayer != value)
        {
            numberPerPlayer = value;
            OnPropertyChanged("NumberPerPlayer");
            OnPropertyChanged("TotalValuePerPlayer");
        }
    }
}

public decimal ChipValue
{
    get { return chipValue; }
    set
    {
        if (chipValue != value)
        {
            chipValue = value;
            //all columns that are based on this need to be updated
            OnPropertyChanged("ChipValue");
            OnPropertyChanged("TotalValuePerPlayer");
    }
}

public decimal TotalValuePerPlayer { get { return chipValue * numberPerPlayer; } }

public void OnPropertyChanged(string propertyName)
{
    if (PropertyChanged != null)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

My 2nd, main question is this: I then have a label which shows the total of all the TotalValuePerPlayer totals (stupidly called TotalTotalValuePerPlayer). I put this in the ChipSet class like this (it iterates through the ObservableCollection and sums the totals):

1            public decimal TotalTotalValuePerPlayer
2            {
3                get { 
4          decimal totalTotalValuePerPlayer = 0;
5                 foreach (Chip chip in chips)
6                  {
7                      totalTotalValuePerPlayer += chip.TotalValuePerPlayer;
8                  }
9                  return totalTotalValuePerPlayer;
10     }
11           }

So - the problem is that when either of the 2 columns (NumberPerPlayer or ChipValue) this field is based on in the UI changes, it does not update.

How do I tell the parent class - ChipSet, which has the TotalTotalValuePerPlayer member to be updated when one of its children class (Chip) members in it's ObservableCollection is updated?

If the TotalTotalValuePerPlayer was in the Chip class I could just notify it when the fields it was based off changed, but it is in the class above it?

Thanks for any advice!

Rodney

+1  A: 

Hi there I believe you are going around this the correct way you just need to go a step further. Here are how would expect the classes to look:

Chip

public class Chip : INotifyPropertyChanged
{
    private int numberPerPlayer;
    public int NumberPerPlayer
    {
        get { return numberPerPlayer; }
        set
        {
            if (numberPerPlayer != value)
            {
                numberPerPlayer = value;
                OnPropertyChanged("NumberPerPlayer");
                OnPropertyChanged("TotalValuePerPlayer");
            }
        }
    }

    private decimal chipValue;
    public decimal ChipValue
    {
        get { return chipValue; }
        set
        {
            if (chipValue != value)
            {
                chipValue = value;
                //all columns that are based on this need to be updated
                OnPropertyChanged("ChipValue");
                OnPropertyChanged("TotalValuePerPlayer");
            }
        }
    }

    public decimal TotalValuePerPlayer 
    { 
        get
        { 
            return chipValue * numberPerPlayer; 
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

ChipSet

public class ChipSet : INotifyPropertyChanged
{
    public ChipSet()
    {
        foreach (var chip in Chips)
        {
            chip.PropertyChanged += (s, e) => { OnPropertyChanged("TotalTotalValuePerPlayer"); };
        }
    }


    public ObservableCollection<Chip> Chips { get; set; }

    public decimal TotalTotalValuePerPlayer
    {
        get
        {
            return Chips.Sum(x => x.TotalValuePerPlayer);
        }
    }

    public void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

I have not tested this fully but it should point you in the right direction. Hope it helps.

Blounty
Hi Blounty,Thanks for taking the time to answer - I am a little confused tho what I need to do - is it the look in the Chipset constructor? chip.PropertyChanged += (s, e) => { OnPropertyChanged("TotalTotalValuePerPlayer"); };Are you hooking up an eventhandler on the Chip class? Would you mind writing that loop out in full as I am not entirely sure what you mean - thanks a lot!
Rodney
Hi Rodney, All the code in the ChipSet Constructor is doing, is looping over the Chips in the ObservableCollection<Chip> and attaching to the PropertyChanged Event of each Chip. This is so when any of the Chip's Properties Change it will update the TotalTotalValuePerPlayer of the ChipSet to the UI.
Blounty
That's awesome, thanks Blounty (took me a while to figure out that syntax - it's new to me!)
Rodney