Question

I've a question. I have a class:

public class ParagonClass : INotifyPropertyChanged
{
    //some variables
    private decimal _totalValue;

    public static ObservableCollection<ParagonClass> paragonLista { get; set; }

    public decimal TotalValue
    {
        get
        {
            if (ProductID > 0)
                _totalValue = Math.Round(ProductCount * PriceBrutto, 2, MidpointRounding.AwayFromZero);
            return _totalValue;
        }
        set
        {
            _totalValue = value;
            NotifyPropertyChanged("TotalValue");
        }
    }

    ...
}

In my window I'd like to bind sum of TotalValue (of all paragonLista elements) to Text of TextBox. I tried few options but with no effect. Best what I get was calculate what I want but only when I opening old, name it document. When I add new position to this document, the value in TextBox doesn't change. I achieved that with:

 private decimal _Sum;
 public decimal Sum
 {
     get
     {
         _Sum = ParagonClass.paragonLista.Sum(x => x.TotalValue);
         return _Sum;
     }
 }

and in .xaml:

<TextBox Name="priceTextBox" FontSize="28" Margin="0,0,5,0" HorizontalAlignment="Right" Text="{Binding Sum, UpdateSourceTrigger=PropertyChanged}" />
Was it helpful?

Solution

Instead of mixing everything together you need two classes.

public class ParagonClass : INotifyPropertyChanged
{
  //some variables
  private decimal _totalValue;

  public decimal TotalValue
  {
    get
    {
        if (ProductID > 0)
            _totalValue = Math.Round(ProductCount * PriceBrutto, 2, MidpointRounding.AwayFromZero);
        return _totalValue;
    }
  // No need for a setter if its calculated
  // See Sheridan's answer for how to do this
  //       set
  //       {
  //           _totalValue = value;
  //           NotifyPropertyChanged("TotalValue");
  //       }
  }

  ...
}

And a collection

public class ParagonCollection : ObservableCollection<ParagonClass>, INotifyPropertyChanged
{
  private int sum;
  public int Sum
  { 
    get{ return sum;} 
    set
    {
      sum = value;
      NotifyPropertyChanged("Sum");
    }
 }
  // You'll need as implantation of INotifyPropertyChanged here
  // and some extra stuff to come
  ...
}

Now we just need to calculate the sum whenever the it changes. There are several times this occurs

  1. When a new Paragon is added to the collection
  2. When a Paragon changes

Lets take them one at a time, and we can hook up the Paragon items being added via the collection by listening for collection changes in the constructor

public ParagonCollection()
{
   // When the collection changes set the Sum to the new Sum of TotalValues
   this.CollectionChanged += OnCollectionChanged;
}

private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
{
  Recalculate();
}

private void Recalculate()
{
  Sum = this.Sum(x=>x.TotalValue);
}

Now if you design your ParagonClass so that the items are immutable (i.e they don't change after they have been created), then you should be all done. However if you need your Paragons to change we need to rewrite what happens when we add or remove an item

private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
{
  foreach(ParagonClass item in args.OldItems)
  {
    // Unsubscribe to changes in each item
    item.PropertyChanged -= OnItemChanged;
  }
  foreach(ParagonClass item in args.NewItems)
  {
    // Subscribe to future changes for each item
    item.PropertyChanged += OnItemChanged;
  }

  Recalculate();
}

private void OnItemChanged(object sender, PropertyChangedEventArgs args)
{
  Recalulate();

  // You might decide that you only want to recalculate for some property 
  // changes, and do something like the following instead
  // if (args.PropertyName=="TotalValue")
  //   Recalulate();
}

OTHER TIPS

In a normal property setter in WPF, you would call NotifyPropertyChanged("PropertyName") to alert the INotifyPropertyChanged interface to the fact that the PropertyName property has been changed and that you want to see the changes in the UI.

In cases like yours, where you have no property setter, you still need to call NotifyPropertyChanged("PropertyName") to see the changes in the UI. Now clearly, you can't call it from the setter as it doesn't have one.

It is most usual to call it from another property setter that plays a part in the output value of the property without the setter. For example, a FirstName property might be used in an Initials property without a setter:

public string Initial
{
    get { return FirstName[0].ToString(); }
}

Now this property will only change when the FirstName property changes, so the sensible place to call NotifyPropertyChanged("Initial") would be in that property:

public string FirstName
{
    get { return _firstName; }
    set
    {
        _firstName= value;
        NotifyPropertyChanged("FirstName");
        NotifyPropertyChanged("Initial");

    }
}

You don't have to call it in any property though... you could also call it wherever you change the associated values:

FirstName = SomeObject.GetFirstName(someIdNumber);
NotifyPropertyChanged("Initial");

So, wherever you call it from, you still need to call the NotifyPropertyChanged method to update the UI.

If you are trying to update TotalValue when the "ProductCount" or "PriceBertto" change, you will need to notify WPF of that change. You could try something like this:

...
public double TotalValue
{
    get
    {
         return Math.Round (ProductCount * PriceBrutto, 2, MidpointRounding.AwayFromZero );
    }
}

private int productCount = 0;
public int ProductCount
{
    get
    {
        return productCount;
    }
    set
    {
        if( Equals( productCount, value) ) return;
        productCount = value;

        NotifyPropertyChange( "ProductCount" );
        NotifyPropertyChange( "TotalValue" );
    }
 }

...

Notice that NotifyPropertyChange for "TotalValue" is invoked from the setter of ProductCount.

I didn't do it all for you, but you can do similar things for your other properties as well. can do the similar things for your other properties that affect TotalValue.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top