I am learning WPF so please bare my question if it is very novice! I have a collection of Items and I want to bind that collection to a Grid and bind the Sum to a textbox. Searching online I found this class that will raise event even when we make change to properties of the collection object.
ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)
But I can't seem to make it work in my code.
Here is my code
SaleItem
public class SaleItem : INotifyPropertyChanged
{
public int Num { get; set; }
public string ItemID { get; set; }
public string Name { get; set; }
private decimal price;
public decimal Price
{
get { return price; }
set
{
this.price = value;
OnPropertyChanged("Total");
}
}
public int quantity;
public int Quantity
{
get { return quantity; }
set
{
this.quantity = value;
OnPropertyChanged("Total");
}
}
public decimal Total
{
get { return decimal.Round(Price * Quantity, 2, MidpointRounding.AwayFromZero);}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Sale
public class Sale : INotifyPropertyChanged
{
private Decimal _total;
public TrulyObservableCollection<SaleItem> Items { get; set; }
public Sale()
{
Items = new TrulyObservableCollection<SaleItem>();
Items.Add(new SaleItem { ItemID = "1", Name = "Product 1", Price = 10, Quantity = 1 });
Items.Add(new SaleItem { ItemID = "2", Name = "Product 2", Price = 10, Quantity = 1 });
Items.Add(new SaleItem { ItemID = "3", Name = "Product 3", Price = 10, Quantity = 1 });
}
public Decimal Total
{
get
{
return Items.Sum(x => x.Total);
}
set
{
_total = value;
OnPropertyChanged("Total");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainWindow
public partial class MainWindow : Window
{
public Sale Model { get; set; }
public MainWindow()
{
InitializeComponent();
Model = new Sale();
this.DataContext = Model;
}
private void btnQuantity_Click(object sender, RoutedEventArgs e)
{
Model.Items.Add(new SaleItem { ItemID = "2", Name = "Product 2", Price = 10, Quantity = 1 });
}
}
XAML
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local ="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:Sale />
</Window.DataContext>
<Grid>
<StackPanel>
<DataGrid x:Name="grdItems" ItemsSource="{Binding Items}"></DataGrid>
<TextBlock x:Name="txtTotal" Text="{Binding Total}"/>
<Button x:Name="btnQuantity" Content="Update" Click="btnQuantity_Click"/>
</StackPanel>
</Grid>
I am trying to test adding an item when I click on the button and also updating the quantity of the Item in the grid. If I set a break point and see the value of Total, then it is correct but somehow it is not updating on the UI. What am I missing here?
What you want to do. Is to call OnPropertyChanged
when the collection itself has been changed. To recalculate the total. At the moment, the Sale
class has no idea that the collection has been updated and that it needs to update its Total
property.
One way you can do this is to check for the NotifyCollectionChangedEvent
like this:
In your constructor for Sale():
public Sale()
{
//Instantiate your collection and add items
Items.CollectionChanged += CollectionChanged;
}
Then add the handler for the event:
public void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("Total");
}
If you're never going to do anything else when the collection changes apart from update the Total property. You can shorten the code further by removing the event handler and using a lambda expression instead:
Items.CollectionChanged += (s,e) => OnPropertyChanged("Total");
Unless you're ever planning on explicitly updating the Total
yourself in the Sale
class. The setter
can be removed:
public Decimal Total
{
get
{
return Items.Sum(x => x.Total);
}
}
In the Sales.cs add:
public void AddItem(SaleItem item)
{
Items.Add(item);
OnPropertyChanged("Total");
}
In MainWindow.xaml.cs call:
Model.AddItem(new SaleItem { ItemID = "2", Name = "Product 2", Price = 10, Quantity = 1 });
Instead of
Model.Items.Add(new SaleItem { ItemID = "2", Name = "Product 2", Price = 10, Quantity = 1 });
This will update the Total.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With