NotifyPropertyChanged of a Calculated property when Items in List Changes - c#

I Have a model SupplierInvoice as follows:
public class SupplierInvoice
{
public bool Use { get; set; }
public ApInvoice Invoice { get; set; }
}
And a ViewModel with a list of this model:
private List<SupplierInvoice> _SupplierInvoices;
public List<SupplierInvoice> SupplierInvoices
{
get
{
return this._SupplierInvoices;
}
set
{
if (this._SupplierInvoices != value)
{
this._SupplierInvoices = value;
this.RaisePropertyChanged("SupplierInvoices");
}
}
}
within this ViewModel I have a calculated property too:
public decimal ApTotal
{
get
{
decimal total = 0;
if (this.SupplierInvoices != null)
{
foreach (SupplierInvoice invoice in this.SupplierInvoices)
{
if (invoice.Use)
{
total += invoice.Invoice.MthInvBal1;
}
}
}
return total;
}
}
this calculated property returns the sum of the balance of all the invoices (if the invoice's Use property is true). The Use property is selected to be true on the view with a checkbox in a grid.
Now... the question is: How do I NotifyPropertyChanged of this calculated property (ApTotal) when the Use property of the SupplierInvoice model has been changed?

I think replacing your List<TObject> by an ObservableCollection<TObject> will do the trick.
From what I remember the List isn't propagating the PropertyChangedEvent to the UI thread.

This may be a bit naughty, but you could always do this:
private List<SupplierInvoice> _SupplierInvoices;
public List<SupplierInvoice> SupplierInvoices
{
get
{
return this._SupplierInvoices;
}
set
{
if (this._SupplierInvoices != value)
{
this._SupplierInvoices = value;
this.RaisePropertyChanged("SupplierInvoices");
this.RaisePropertyChanged("ApTotal");
}
}
}

Whenever you have a calculated property, you just need to raise the INotifyPropertyChanged.PropertyChanged event from the other properties that are involved in the calculation. So as your ApTotal property is calculated from just the SupplierInvoices property, then you can just notify the interface from that property setter:
public List<SupplierInvoice> SupplierInvoices
{
get
{
return this._SupplierInvoices;
}
set
{
if (this._SupplierInvoices != value)
{
this._SupplierInvoices = value;
this.RaisePropertyChanged("SupplierInvoices");
this.RaisePropertyChanged("ApTotal");
}
}
}

Related

WPF: Property changed on List<Object>, will that affect its own class of another property..?

When I change the value of any DenomList item, how to affect the Total value?
This I tried and modifying the list item is not affecting the TotalValue property.
int _totalValue;
public int TotalValue
{
get { return _totalValue;}
set {
_totalValue = value;
RaisePropertyChanged("TotalValue");
}
}
List<MarkDenom> _denomList;
public List<MarkDenom> DenomList
{
get { return _denomList; }
set
{
this._denomList = value;
TotalValue = _denomList.Select(o => o.Total).Sum();
RaisePropertyChanged("DenomList");
}
}
int? _totalValue;
public int TotalValue
{
get
{
return _totalValue ??= _denomList.Select(o => o.Total).Sum();
}
}
List<MarkDenom> _denomList;
public List<MarkDenom> DenomList
{
get { return _denomList; }
set
{
this._denomList = value;
_totalValue = null;
RaisePropertyChanged("DenomList");
}
}
From what I've got from your code, the TotalValue property shouldn't be changed other than in case if DenomList value is changed. So here is upgraded code that allows calculating TotalValue on demand instead of on each change of DenomList. You may also want to force TotalValue recalculation after the list content changes, to do so just remove the backing field int? _totalValue completely, leaving just the function behind the property: public int TotalValue => _denomList.Select(o => o.Total).Sum();

.NET bind checked property of checkbox with SqlByte datatype

This fails:
dtaAutomaticFlag.DataBindings.Add("Checked", BndSource, "AutomaticFlag");
most probably because AutomaticFlag is defined as:
public SqlByte AutomaticFlag { get; set; }
SQL Server is tinyint. The reason is that i dont only need 0 and 0 as values.
I need to be checked if the value is 1 , unchecked for ALL other values.
One thing that i thought is to create an extra property AutomaticFlagBoolean in my class and check the AutomaticFlag and bind to the new property.
This is what i tried but i get always false:
private SqlByte _automaticFlag;
public SqlByte AutomaticFlag
{
get { return _automaticFlag; }
set
{
if (_automaticFlagBoolean)
{
_automaticFlag = 1;
}
else
{
_automaticFlag = 0;
}
}
}
private bool _automaticFlagBoolean;
public bool AutomaticFlagBoolean
{
get
{
if (_automaticFlag == 1)
{
return _automaticFlagBoolean = true;
}
else
{
return _automaticFlagBoolean = false;
}
}
set { _automaticFlagBoolean = value; }
}
this is in my binding:
dtaAutomaticFlag.DataBindings.Add("Checked", BndSource, "AutomaticFlagBoolean");
The checkbox control expects a boolean value for binding.
Try using a local variable to hold the value:
private SqlByte _automaticFlag;
private bool _automaticFlagBoolean;
public SqlByte AutomaticFlag
{
get { return _automaticFlag; }
set
{
_automaticFlag = value;
_automaticFlagBoolean = (_automaticFlag != 0);
}
}
public bool AutomaticFlagBoolean
{
get
{
return _AutomaticFlagBookean;
}
set { _automaticFlagBoolean = value;
if (_automaticFlagBoolean) {
_automaticFlag = 1;
} else {
_automaticFlag = 0;
}
}
}
Then bind as so:
dtaAutomaticFlag.DataBindings.Add("Checked", BndSource, "AutomaticFlagBoolean");
Your class should also implement INotifyPropertyChanged as well but that's a little beyond the scope of your question.

RaisePropertyChanged not updating UI

I'm having trouble getting my UI to update Two Listboxes' to update properly when my ViewModel changes.
First, the basic logic behind the page:
Movie is an object with a title, and a variety of MovieDetails. Some MovieDetail are different than others, as they are detailed which is a glorified way of saying they're more Important.
I use two ListBoxes to separate these MovieDetails into stacked ListBoxes, one for 'Detailed' and one for 'NotDetailed'
If a movie has no 'Detailed' attributes, the corresponding list is Hidden via a BooleanToVisibilityConverter (and vice-versa)
When I navigate to the page, I set the Movie the page corresponds to, and it should RaisePropertyChanged to alert the AllMoviesDetail ObservableCollection that it should re-get Movies.MovieDetailFetchedList.
From there, AllMoviesDetail would alert the two ObservableCollections (Detailed, NotDetailed) they should be re-get.
In fact, RaisePropertyChanged on NotDetailedMovieDetails or DetailedMovieDetails does not seem to do anything either. (And the corresponding HasNotDetailedMovieDetails, Has...)
What does work, however, is if I add more items into the list, the CollectionChanged event seems to fire and reactivate the list. I have also been able to do this by instantiating the ObservableCollections in code first var temp = DetailedMoviesDetail;
public class MoviesDetailViewModel : ViewModelBase
{
#region Property Names
public const string MoviePropertyString = "Movie";
public const string AllMoviesDetailPropertyString = "AllMoviesDetail";
public const string DetailedMoviesDetailPropertyString = "DetailedMoviesDetail";
public const string NotDetailedMoviesDetailPropertyString = "NotDetailedMoviesDetail";
public const string HasNotDetailedMoviesDetailPropertyString = "HasNotDetailedMoviesDetail";
public const string HasDetailedMoviesDetailPropertyString = "HasDetailedMoviesDetail";
public const string NotDetailedHeaderPropertyString = "NotDetailedHeader";
#endregion
public MoviesDetailViewModel()
{
if (IsInDesignMode)
{
Movie = DesignDataStore.MovieList[0];
Movie.Category = Category.DDA;
}
}
private Movie _Movie;
/// <summary>
/// The Movie for which to browse MoviesDetail. It is expected when setting this property, that MoviesDetail for it have been downloaded previously.
/// </summary>
/// <remarks>The 'Master' property for this ViewModel. All properties are Dependent on this and the underlying property MoviesDetailList</remarks>
/// <seealso cref="MovieDetailFetchedList"/>
public Movie Movie
{
get { return _Movie; }
set
{
if (_Movie != value)
{
if (_Movie != null)
_Movie.MovieDetailFetchedList.CollectionChanged -= MoviesDetailListChanged;
_Movie = value;
RaisePropertyChanged(MoviePropertyString);
RaisePropertyChanged(StatementPeriodAvailablePropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
Movie.MovieDetailFetchedList.CollectionChanged += MoviesDetailListChanged;
RaisePropertyChanged(AllMoviesDetailPropertyString);
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedHeaderPropertyString);
}
}
}
private void MoviesDetailListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (var item in e.NewItems)
{
if (((MovieDetail) item).IsDetailed())
DetailedMoviesDetail.Add(item as MovieDetail);
else
NotDetailedMoviesDetail.Add(item as MovieDetail);
}
}
else
{
RaisePropertyChanged(AllMoviesDetailPropertyString);
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
}
}
#endregion
private MovieDetailFetchedList _allMoviesDetail;
public MovieDetailFetchedList AllMoviesDetail
{
get
{
if (Movie == null)
return new MovieDetailFetchedList();
return _allMoviesDetail ?? (AllMoviesDetail = Movie.MovieDetailFetchedList);
}
set
{
if (_allMoviesDetail != value)
{
if (_allMoviesDetail != null)
_allMoviesDetail.CollectionChanged -= MoviesDetailListChanged;
_allMoviesDetail = value;
_allMoviesDetail.CollectionChanged += MoviesDetailListChanged;
RaisePropertyChanged(AllMoviesDetailPropertyString);
//force update
DetailedMoviesDetail = NotDetailedMoviesDetail = null;
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasDetailedMoviesDetailPropertyString);
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasNotDetailedMoviesDetailPropertyString);
}
}
}
public bool HasNotDetailedMoviesDetail { get { return NotDetailedMoviesDetail != null && NotDetailedMoviesDetail.Count > 0; } }
private ObservableCollection<MovieDetail> _notDetailedMoviesDetail;
public ObservableCollection<MovieDetail> NotDetailedMoviesDetail
{
get
{
if (Movie == null) return new ObservableCollection<MovieDetail>();
return AllMoviesDetail;
return _notDetailedMoviesDetail ?? //make sure RaisePropertyChanged happens by using property setter
(NotDetailedMoviesDetail = AllMoviesDetail.Where(mem => !mem.IsDetailed()).ToObservableCollection());
}
set
{
_notDetailedMoviesDetail = value;
RaisePropertyChanged(NotDetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasNotDetailedMoviesDetailPropertyString);
}
}
public bool HasDetailedMoviesDetail
{ get { return DetailedMoviesDetail != null && DetailedMoviesDetail.Count > 0; } }
private ObservableCollection<MovieDetail> _DetailedMoviesDetail;
public ObservableCollection<MovieDetail> DetailedMoviesDetail
{
get
{
if (Movie == null) return new ObservableCollection<MovieDetail>();
return AllMoviesDetail;
return _DetailedMoviesDetail ?? //make sure RaisePropertyChanged happens by using property setter
(DetailedMoviesDetail = AllMoviesDetail.Where(mem => mem.IsDetailed()).ToObservableCollection());
}
set
{
_DetailedMoviesDetail = value;
RaisePropertyChanged(DetailedMoviesDetailPropertyString);
RaisePropertyChanged(HasDetailedMoviesDetailPropertyString);
}
}
private string _DetailedHeader;
public string DetailedHeader
{
get { return _DetailedHeader ?? (_DetailedHeader = AppResources.in_available); }
set { _DetailedHeader = value; }
}
public string NotDetailedHeader
{
get { return (Movie != null && Movie.Category == Category.DRAMA) ? AppResources.Movie_MoviesDetail : AppResources.not_in_available; }
}
}
All of your property getters (except AllMoviesDetail) have two return statements. Since only the first will be executed, the values are not being assigned and the PropertyChanged events are not being twiggered.

GridView DataBinding

I have a problem with a GridView(I'm using Telerick but i think the .NET GridView is similar for this situation).
I have a List that contains some user define object with some properties that will be displayed in a GridView. This list is loaded from SQL.
My problem is that i have an int property that i want to be parsed and displayed in GridView with some strings.
public class Vehicles
{
private int id;
public int Id
{
get { return id; }
set { id = value; }
}
private string vehName;
public string VehName
{
get { return vehName; }
set { vehName = value; }
}
private int gpsInterval;
public int GpsInterval
{
get { return gpsInterval; }
set { gpsInterval = value; }
}
private int isStolen;
public int IsStolen
{
get { return isStolen; }
set { isStolen = value; }
}
...
}
...
List<Vehicles> vehs = DBveh.getAllVehicles();
GridViewUnitsList.DataSource = vehs;
Is stolen is curently displayed as an int in the GridView. So is there a method to parse "isStolen" value and replace it with somenting like "YES"/"NO" without using a foreach and iterating throw the hole GridView after the binding?
There are 2 easy options:
1) Add a property to your object and reference that property in the DataGrid:
public string IsStolenStr
{
get { return isStolen == 1? "Yes" : "No"; }
}
2) Or add the logic to a <asp:template> column in the DataGrid:
<%# Eval("IsStolen") == 1 ? "Yes" : "No" %>
I would modify the SQL statement so that it returned the Yes/No string based on the isStolen value.

C# StackOverflowException

Problem: I am trying to update a List. If a certain item's ID already exists in the List, I want to add onto that item's quantity. If not, then I want to add another item to the list.
cart = (List<OrderItem>)Session["cart"];
for(int counter = cart.Count-1; counter >= 0; counter--)
{
if (cart[counter].productId == item.productId)
{
cart[counter].productQuantity += item.productQuantity;
}
else if (counter == 0)
{
cart.Add(item);
}
}
cart[counter] and item represent an instance(s) of a custom object of mine. Currently when I finally find a matching ID, everything APPEARS as though it should work, but I get a StackOverflowException thrown in my custom object class.
public int productQuantity
{
get
{
return _productQuantity;
}
set
{
productQuantity = value;
}
}
It gets thrown right at the open-bracket of the "set". Could somebody please tell me what the heck is wrong because I've been going at this for the past 2+ hours to no avail. Thank you in advance.
the problem is in your setter of the productQuantity
it should read:
set
{
_productQuantity= value;
}
edit (naming convention):
public class Vertex3d
{
//fields are all declared private, which is a good practice in general
private int _x;
//The properties are declared public, but could also be private, protected, or protected internal, as desired.
public int X
{
get { return _x; }
set { _x = value; }
}
}
Replace productQuantity = value; with _productQuantity = value; (you're recurring infinitely by calling the setter over and over)
Why not just use this instead?
public int productQuantity { get; set; }
But the flaw was in the _
public int productQuantity {
get {
return _productQuantity;
}
set {
_productQuantity = value;
}
}
cart = (List<OrderItem>)Session["cart"];
int index = cart.Find(OrderItem => OrderItem.productId == item.productId);
if(index == -1) {
cart.Add(item);
} else {
cart[index].productQuantity += item.productQuantity;
}
public int productQuantity
{
get
{
return _productQuantity;
}
set
{
_productQuantity = value; //this should be an assignment to a member variable.
}
}

Categories

Resources