title: ObservableCollection.CollectionChanged not firing
body: <p>I have the following interface:</p>
  
  <p><img src="https://i.stack.imgur.com/17xTX.png" alt="DataGrid"></p>
  
  <p>When an item is added into the <code>DataGrid</code>, the <code>Total column</code> will update according to (price * quantity), and the total <code>TextBox</code> will also have the total of all the rows added.</p>
  
  <p>However, when I change the quantity of a row, the <code>Total column</code> updates, but the total <code>TextBox</code> does not.</p>
  
  <p>Here is my code, thanks in advance.</p>
  
  <pre><code> public partial class pgCheckout : Page {
  
      ObservableCollection&lt;SaleItem&gt; items = new ObservableCollection&lt;SaleItem&gt;();
  
      public pgCheckout() {
          InitializeComponent();
          dgItems.ItemsSource = items;
          dgItems.Loaded += SetMinWidths;
          items.CollectionChanged += setTotal;
      }
  
      public void setTotal(object source, EventArgs e) {
          decimal total = 0;
          foreach(SaleItem i in items) {
              total += i.Total;
          }
  
          txtTotal.Text = total.ToString();
      }
  
  
      public void SetMinWidths(object source, EventArgs e) {
          foreach (var column in dgItems.Columns) {
              if (column.DisplayIndex != 0) {
                  column.MinWidth = column.ActualWidth;
                  column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
              }
          }
      }
  
      private void btnRemove_Click(object sender, RoutedEventArgs e) {
          items.RemoveAt(dgItems.SelectedIndex);
      }
  
      private void btnAdd_Click(object sender, RoutedEventArgs e) {
          bool exist = false;
          foreach (SaleItem i in items) {
              if (i.ItemID.Equals(txtItemID.Text))
                  exist = true;
          }
  
          if (exist) {
              lblErr.Content = "Item already exist";
              txtItemID.Text = "";
          }
  
          else {
  
              using (var db = new PoSEntities()) {
                  var query = from i in db.Items
                              where i.ItemID.Equals(txtItemID.Text.Trim())
                              select i;
                  var itm = query.FirstOrDefault();
                  if (itm == null) {
                      lblErr.Content = "Invalid Item";
                      txtItemID.Text = "";
                  }
                  else {
                      txtItemID.Text = "";
                      lblErr.Content = "";
                      items.Add(new SaleItem() {
                          Num = items.Count + 1,
                          ItemID = itm.ItemID,
                          Name = itm.Name,
                          Price = decimal.Round(itm.Price, 2, MidpointRounding.AwayFromZero),
                          Quantity = 1,
                      });
                  }
              }
          }
      }
  
      private void txtItemID_KeyUp(object sender, KeyEventArgs e) {
          if (e.Key == System.Windows.Input.Key.Enter) {
              btnAdd_Click(txtItemID, e);
          }
      }
  }
  
  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));
          }
      }
  }
  </code></pre>