Updating Total causing infinite loop - c#

I current have 3 telerik:RadMaskedCurrencyInput.
The first one is the principal
The First and Second are the disbursals
At the moment I have it set up such that principal = First disbursal + Second disbursal.
I'm trying to have a case such that if I update principal it updates First Disbursal and sets second disbursal to zero. So Principal = DisbursalOne and DisbursalTwo = 0
// Pv is principal
private decimal?_pv;
public decimal? Pv { get { return _pv; } set { _pv = value; OnPropertyChanged("Pv"); } }
private decimal? _disbursalOne;
public decimal? DisbursalOne
{
get
{
return _disbursalOne;
}
set
{
_disbursalOne = value;
if (_disbursalOne != null)
DisbursalTotal = _disbursalOne + DisbursalTwo;
else
{
_disbursalOne = 0;
}
OnPropertyChanged("DisbursalOne");
}
}
Disbursal Two essentially almost identical to Disbursal one so the Code is not necessary.
private decimal? _disbursalTotal;
public decimal? DisbursalTotal
{
get { return _disbursalTotal; }
set
{
_disbursalTotal = value;
if (_disbursalTotal != null) UpdateDisbursalTotal(_disbursalTotal);
else UpdateDisbursalTotal(0);
Pv = _disbursalTotal;
OnPropertyChanged("DisbursalTotal");
}
}
I apologize for the bad title.
// Updates the Total Disbursed Fields on the UI
public void UpdateDisbursalTotal(decimal? Total)
{
var cultureInfo = Thread.CurrentThread.CurrentCulture; // You can also hardcode the culture, e.g. var cultureInfo = new CultureInfo("fr-FR"), but then you lose culture-specific formatting such as decimal point (. or ,) or the position of the currency symbol (before or after)
var numberFormatInfo = (NumberFormatInfo)cultureInfo.NumberFormat.Clone();
numberFormatInfo.CurrencySymbol = "$"; // Replace with "$" or "£" or whatever you need
Double _total = (double) Total;
DisburseString = _total.ToString("C", numberFormatInfo);
}
private string _disburseString;
public string DisburseString { get { return _disburseString; } set { _disburseString = value; OnPropertyChanged("DisburseString"); } }

It's usually a good idea to format your PropertyChanged events in the following way, that way if the same value gets set, you don't notify anyone as nothing has actually changed. If things are relying on your PropertyChanged events to update each other, this should break the cycle:
private decimal? _disbursalTotal;
public decimal? DisbursalTotal
{
get { return _disbursalTotal; }
set
{
if (value != _disburseTotal) {
_disbursalTotal = value;
if (_disbursalTotal != null) UpdateDisbursalTotal(_disbursalTotal);
else UpdateDisbursalTotal(0);
Pv = _disbursalTotal;
OnPropertyChanged("DisbursalTotal");
}
}
}

Step through the code. My guess is that OnPropertyChanged event also changes a property. You can check if the property is actually changing (or just being set to the same value) before calling OnPropertyChanged.

Related

NotifyPropertyChanged of a Calculated property when Items in List Changes

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");
}
}
}

How can I clear/prevent binding to datagrid if duplicate exists?

Here's my property that determines if I should bind the other properties or not:
public string LotNumber {
get {
return lotNumber;
}
set {
using (var db = new DDataContext()) {
lotNumber = value;
// Check for duplicates
bool isDuplicate =
db.LotInformation.Any(r => r.lot_number == lotNumber);
if (isDuplicate == true) {
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
lotNumber = null;
// Inform user that the lot_number already exists
errorWindow.Message =
LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(
LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(
LanguageResources.Resource.Lot_Exists_Already);
return;
} else {
lotNumber = value;
}
RaisePropertyChanged("LotNumber");
}
}
}
My problem right now is if I upload a file and if the lot number already exists in the database, the boolean returns true and an error message is thrown. However, after that,it loops again and then the boolean is set to false since now the value is null and it still binds the data afterward. How can I break out of the loop and just make it stop running/clear/prevent binding when bool is true in the case above?
I assume you have some code like this:
LotNumber = "ABC5"; // ABC5 already exists in the database - uh oh!
And then you're trying to figure everything out in the "setter". It's already too late by that point. Instead, move your logic into separate methods:
private bool LotNumberExists(string lotNumber)
{
using (var db = new DDataContext())
{
return db.LotInformation.Any(r => r.lot_number == lotNumber);
}
}
private void ClearFields()
{
ComponentsList = null;
FamiliesList = null;
ExpirationDate = null;
LotNumber = null;
}
private void InformUserOfDuplicate()
{
// Inform user that the lot_number already exists
errorWindow.Message = LanguageResources.Resource.Lot_Exists_Already;
dialogService.ShowDialog(LanguageResources.Resource.Error, errorWindow);
logger.writeErrLog(LanguageResources.Resource.Lot_Exists_Already);
}
Then check the return value of that method before setting LotNumber.
private void SomeOtherMethod()
{
string someLotNumber = "ABC5";
if (LotNumberExists(someLotNumber)
{
ClearFields();
InformUserOfDuplicate();
return;
}
LotNumber = someLotNumber;
}
Turn your setter back into a simple setter without a ton of logic wrapped up in it:
public string LotNumber
{
get { return lotNumber; }
set
{
lotNumber = value;
RaisePropertyChanged("LotNumber");
}
}

DataBindings in Winform-Custom Property Didn't work

For compatible Null i covered Value property. But this property cannot use in DataBindings. it's not change when control's value update. I change the control to DateTimePicker,everything is fine.What's wrong with the Value property?
Test Class
class prod
{
int id;
public int Id {
get { return id; }
set { id = value; }
}
DateTime? md;
public Nullable<DateTime> Md {
get { return md; }
set { md = value; }
}
}
//Custom DateTimePicker
[Bindable(true), Browsable(true)]
public new object Value
{
get
{
if (realDate)
{
return base.Value;
}
else
{
return DBNull.Value; //If not a real date, sent DBNull to the bound field
}
}
set
{
if (Convert.IsDBNull(value))
{
realDate = false;
oldFormat = Format; //Store the Format of the datetimepicker
Format = DateTimePickerFormat.Custom;
CustomFormat = " "; //With this custom format, the datetimepicker is empty
}
else
{
realDate = true;
CustomFormat = null;
base.Value = Convert.ToDateTime(value);
}
OnValueChanged();
}
}
//Binding Code:
prod pp=new prod();
datePicker1.DataBindings.Add("Value",pp,"Md",true,DataSourceUpdateMode.OnValidation);
i found the problem----need use WriteValue for each databinding in selectitemchanged.
use DataSourceUpdateMode.OnPropertyChanged instead of DataSourceUpdateMode.OnValidation

populating datagridview with list of objects

I have a List that contains a series of transaction objects. What I'm trying to do is to display these transaction objects in a Datagridview control on loading a form, basically the Datagridview should represent something of a transaction register to display the data for each of the transaction objects in the list.
I must admit to a lack of experience when it comes to using Datagridviews and I'm having some difficulty with understanding what I need to do here.
My question is, how do I go about getting the details of each of the objects in the list to display in the Datagridview?
Here is my code.
First the transaction class:
public class Transaction
{
// Class properties
private decimal amount;
private string type;
private decimal balance;
private string date;
private string transNum;
private string description;
// Constructor to create transaction object with values set.
public Transaction(decimal amount, string type, decimal currBal, string date, string num, string descrip)
{
this.amount = amount;
this.type = type;
this.balance = currBal;
this.date = date;
this.transNum = num;
this.description = descrip;
}
// Get and Set accessors to allow manipulation of values.
public decimal Amount
{
get
{
return amount;
}
set
{
amount = value;
}
}
public string Type
{
get
{
return type;
}
set
{
type = value;
}
}
public decimal Balance
{
get
{
return balance;
}
set
{
balance = value;
}
}
public string Date
{
get
{
return date;
}
set
{
date = value;
}
}
public string TransNum
{
get
{
return transNum;
}
set
{
transNum = value;
}
}
public string Description
{
get
{
return description;
}
set
{
description = value;
}
}
public decimal addCredit(decimal balance, decimal credit)
{
decimal newBalance;
newBalance = balance + credit;
return newBalance;
}
public decimal subtractDebit(decimal balance, decimal debit)
{
decimal newBalance;
newBalance = balance - debit;
return newBalance;
}
}
}
Now the code for the "Register" form:
public partial class Register : Form
{
List<Transaction> tranList = new List<Transaction>();
public Register(List<Transaction> List)
{
InitializeComponent();
this.tranList = List;
}
private void Register_Load(object sender, System.EventArgs e)
{
//regView represents the Datagridview that I'm trying to work with
regView.AutoSize = true;
regView.DataSource = tranList;
regView.Rows.Add(tranList[0]);
}
}
And here's the output I get.
There's really two high level approaches to this.
1) Add the manually created rows directly to the DataGridView. In this case, you have to manually update/remove them as things change. This approach is "ok" if you don't intend to alter/change the content of the display after you initialize it. It becomes untenable if you do.
To add it directly, you need to create a DataGridViewRow, and populate it with the individual values, and then add the DataGridViewRow to the DataGridView.Rows.
2) Data bind the DGV. There's many articles about databinding to a DataGridView. In some cases, it's easier to just add your data to a DataTable, and then extract a DataView from that, and bind the DataGridView to the DataView. Other people find it easier to directly bind to a collection.
CodeProject has a decent article to get you started down that path, but a quick Google search will yield many other articles.
http://www.codeproject.com/Articles/24656/A-Detailed-Data-Binding-Tutorial
use as DGV:
DataGridView groupListDataGridView;
column:
DataGridViewTextBoxColumn groupListNameColumn;
column setup should be like this:
groupListNameColumn.DataPropertyName = "name";
use this property, else all columns will be added.
groupListDataGridView.AutoGenerateColumns = false;
populate like this:
private void populateGroupList() {
groupListDataGridView.DataSource = null;
formattedGroupList = new SortableBindingList<DataGridGroupObject>();
foreach (GroupObject go in StartUp.GroupList) {
DataGridGroupObject dggo = new DataGridGroupObject();
dggo.id = go.Id;
dggo.name = go.Name;
formattedGroupList.Add(dggo);
}
groupListDataGridView.DataSource = formattedGroupList;
groupListDataGridView.Invalidate();
}
and model:
public class DataGridGroupObject
{
public int id { get; set; } //this will be match id column
public string name { get; set; } // this will be match name column
}
Simply add using System.Linq; at the top. Then you can do this:
//This will create a custom datasource for the DataGridView.
var transactionsDataSource = tranList.Select(x => new
{
Amount = x.amount,
Type = x.type,
Balance = x.balance,
Date = x.date,
TransNum = x.transNum
Description = x.description
}).ToList();
//This will assign the datasource. All the columns you listed will show up, and every row
//of data in the list will populate into the DataGridView.
regView.DataSource = transactionsDataSource;

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