How to roll back to original value iPropertyChanged? C# - c#

I have a datagrid in my xamarin form app and it got a editable column. The values in the column are from MySql database and user can change the value and store to db. I used IPropertyChanged interface to allow user make the changes to the value. There is one condition when editing the value. The new value must be equal or bigger than the original value. My problem is whenever I enter a value bigger than the original, I cannot edit the value again to previous value. For example, the original value is 10. The new value I enter is 30. If I want to change the value again and this time I set it to 20, it is not allowing me because now the original value is 30 not 10 and 20 is less than 30. How can I retain the original value and compare with it?
public int ActualReading
{
get
{
return _ActualReading;
}
set
{
if (value >= _ActualReading)
{
_ActualReading = value;
RaisePropertyChanged("ActualReading");
}
else
{
UserDialogs.Instance.Alert("Meter readings should not be smaller than previous value.","Error","Ok");
}
}
}
private void RaisePropertyChanged(String Name)
{
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(Name));
}

You have to store the original value. I'm using the following pattern.
Assuming you have a model like this
public class Model
{
public int ActualReading {get; set;}
}
and a viewmodel like this (I removed the INotifyPropertyChanged part for better reading)
public class ViewModel
{
private readonly Model MyModel;
private int _actualReading;
public int ActualReading
{
get { return _actualReading; }
set { _actualReading = value; }
}
public ViewModel(Model model)
{
MyModel = model;
ActualReading = model.ActualReading;
}
public Model GetModel()
{
MyModel.ActualReading = ActualReading;
return MyModel;
}
}
When you create the ViewModel instances you initialize it with the coresponding Model instance. When you have implemented this you can add your check in an easy way like this.
private int _actualReading;
public int ActualReading
{
get { return _actualReading; }
set
{
if (value >= MyModel.ActualReading)
{
_actualReading = value;
}
else
{
UserDialogs.Instance.Alert("Meter readings should not be smaller than previous value.", "Error", "Ok");
}
}
}

Related

C# Calculate total price with datetime and price with live update in wpf

I have a wpf application in C# which is used to book houses.
I want to calculate the total price of a reservation. I have 2 datetimes: Check in & Check Out.
The calculation I need to use = (CheckOut - CheckIn).TotalDays * BnB.Price
BnB.Price is the price per night.
I want the totalprice to update live in the guest View.
So whenever I change the check in and out dates the total price automatically changes.
This is my Reservation Model:
private double _totalprice { get; set; }
private DateTime _checkIn { get; set; }
private DateTime _checkOut { get; set; }
public double TotalPrice
{ get => _totalprice; set { _totalprice = value; Notify("TotalPrice"); } }
public DateTime CheckIn
{ get => _checkIn; set { if (value < _checkOut) { _checkIn = value; }; Notify("CheckIn"); } }
public DateTime CheckOut
{ get => _checkOut; set { if (value > _checkIn) { _checkOut = value; }; Notify("CheckOut"); } }
If I missed something please let me know!
Based on the comments in your question, BnB is a member of the reservation model. Given that, you can change TotalPrice to a readonly property, a property without a set method, and just caluclate the total in the get method. Then, in the setters for CheckIn and CheckOut, call Notify("TotalPrice") to update the UI. You also need to call Notify("TotalPrice") in the setter for BnB, since that member participates in the calculation of TotalPrice.
This assumes that the Reservation Model implements INotifyPropertyChanged and that Notify invokes the PropertyChanged event.
public class Reservation : INotifyPropertyChanged
{
private BnB? _bnb;
private DateTime _checkIn;
private DateTime _checkOut;
public double TotalPrice
{
// Instead of using a field, just calculate TotalPrice on the fly.
get
{
// Check for nulls - since _bnb is the only thing that is
// is nullable in the calculation, it is the only thing we
// need to check.
if (BnB== null)
{
return 0;
}
return (CheckOut - CheckIn).TotalDays * BnB.Price;
}
}
public BnB BnB
{
get => _bnb;
set
{
if (_bnb != value)
{
_bnb = value;
Notify(nameof(BnB));
Notify(nameof(TotalPrice));
}
}
}
public DateTime CheckIn
{
get => _checkIn;
set
{
if (value != _checkIn && value < _checkOut)
{
// I've changed the check to ensure that the new value isn't the
// same as the current value. No sense running this code if nothing
// has actually changed.
_checkIn = value;
}
// Without knowing what Notify does exactly, I can't say for sure. But
// if it is only invoking the PropertyChanged event then these two lines
// should probably be placed inside the if statement. There isn't any
// point to notifying the UI that a change happened if a change didn't
// actually happen.
Notify(nameof(CheckIn));
// You can notify the UI to update TotalPrice within a setter for a
// different property.
Notify(nameof(TotalPrice));
}
}
public DateTime CheckOut
{
get => _checkOut;
set
{
if (value != _checkOut&& value > _checkIn)
{
_checkOut = value;
}
// These might make more sense inside the if statement as well.
Notify(nameof(CheckOut));
Notify(nameof(TotalPrice));
}
}
// Other members of the Reservation model.
}
If you really need to have TotalPrice be a property with a getter / setter and a backing field, then just set the TotalPrice property when you set CheckIn, CheckOut, or BnB.
public class Reservation
{
private BnB? _bnb;
private DateTime _checkIn;
private DateTime _checkOut;
private double _totalPrice;
public double TotalPrice
{
get => _totalPrice;
set
{
if (_totalPrice != value)
{
_totalPrice = value;
Notify("TotalPrice");
}
}
}
public BnB BnB
{
get => _bnb;
set
{
if (_bnb != value)
{
_bnb = value;
Notify(nameof(BnB));
TotalPrice = CalculateTotalPrice();
}
}
}
public DateTime CheckIn
{
get => _checkIn;
set
{
if (value != _checkIn && value < _checkOut)
{
// I've changed the check to ensure that the new value isn't the
// same as the current value. No sense running this code if nothing
// has actually changed.
_checkIn = value;
}
// Without knowing what Notify does exactly, I can't say for sure. But
// if it is only invoking the PropertyChanged event then these two lines
// should probably be placed inside the if statement. There isn't any
// point to notifying the UI that a change happened if a change didn't
// actually happen.
Notify(nameof(CheckIn));
// You can notify the UI to update TotalPrice within a setter for a
// different property.
TotalPrice = CalculateTotalPrice();
}
}
public DateTime CheckOut
{
get => _checkOut;
set
{
if (value != _checkOut&& value > _checkIn)
{
_checkOut = value;
}
// These might make more sense inside the if statement as well.
Notify(nameof(CheckOut));
TotalPrice = CalculateTotalPrice();
}
}
private double CalculateTotalPrice()
{
if (BnB== null)
{
return 0;
}
return (CheckOut - CheckIn).TotalDays * BnB.Price;
}
// Other members of the Reservation model.
}
Also, on a side note you might want to consider switching from double to decimal to represent currency.

Finding the items changed in Observable Collection

I have a class from a EF db context which I have displayed in a datagrid based on an ObservableCollection. The user can edit the the grid and this all displays fine.
However I now need to send the data back to the database. I do not want to send all the items in the collection to my save method, so can I find only the items that have been have change in the collection?
just as an idea (not professing this to be an ideal solution) i have run into a similar issue, looked around for potential solutions and none of those were exactly what i wanted.
i had to pass a collection to WPF DataGrid and it seemed to complain about using List, hence i turned to ObservableCollection
i did not want to work directly with the EF context for multiple reasons primarily because i wanted to grab items and pass them to intermediate transaction factory to be processed (business logic).
so decided to stick with ObservableCollection and instead make slight modification to the ViewModel since this i was free to do it.
my model ended up to look like this:
internal class databaseItemModel
{
int _id;
string _description;
decimal _price;
decimal _quantity;
decimal _cost;
bool _modified;
public databaseItemModel()
{
_modified = false;
}
public int id { get { return _id; } }
public bool modified { get { return _modified; } }
public string description { get { return _description; } set { _description = value; _modified = true; } }
public decimal price { get { return _price; } set { _price = value; _modified = true; } }
public decimal quantity { get { return _quantity; } set { _quantity = value; _modified = true; } }
public decimal cost { get { return _cost; } set { _cost = value; _modified = true; } }
public bool selected { get; set; }
public void setId(int _idvalue)
{
_id = _idvalue;
}
public decimal value
{
get { return price * quantity; }
}
public void setDescription(string _descr)
{
_description = _descr;
}
public void setPrice(decimal _pr)
{
_price = _pr;
}
public void setQuantity(decimal _qty)
{
_quantity = _qty;
}
public void setCost(decimal _cst)
{
_cost = _cst;
}
}
Basically, the plain idea behind it is that i would use functions to populate data rather than using properties direct and then pass the item to ObservableCollection which then would become the source for the DataGrid.ItemsSource
since DataGrid/ObservableCollection would work with properties - modified objects would be marked as modified and i would then be able to pick up the collection on exit and collect the modified items.
hope this is helpful.
You can use NotifyCollectionChangedAction to detect which items has been changed in the ObservableCollection
However, just Jens said, the best way would be let the EF handle it for you.
Cheers.
ObservableCollection<int> listOfObject = new ObservableCollection<int>() { 1, 2, 3, 4};
listOfObject.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
delegate (object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
Console.WriteLine($"{e.NewItems[0]} just been added to the list at index = {e.NewStartingIndex}");
}
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Replace)
{
Console.WriteLine($"Replace item {e.OldItems[0]} with {e.NewItems[0]}");
}
}
);
listOfObject.Add(1);
listOfObject[2] = 3;
listOfObject[3] = 1;
Output:
1 just been added to the list at index = 4
Replace item 3 with 3
Replace item 4 with 1

Forcing all WPF bound controls to update with GetBindingExpression().UpdateTarget()

I have a WPF application that includes ~50 controls that are bound to properties on my business object which implements INotifyPropertyChanged. Here's a quick snippet of my business object:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// properties begin here
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
// constructor, etc. not shown
}
I also have several validation rules that are used to validate the user input in these controls. I'm using command binding to prevent my user from saving the data if there are any validation errors. My application also includes a "Reset default values" button which, obviously, will reset the default value for all of the properties on my business object. This all works exactly as I'd like it to with one exception. If my user enters invalid data into one or more controls and then clicks the "Reset default values" button, the controls that contain invalid data don't always update as I'd expect. This happens because of the following code in my property setters:
if (_name == value)
{
return;
}
This code exists to prevent unnecessary property changed notifications from occurring when the value entered by my user in the bound UI control is the same value that the property is already set to. As an example, I have an IntegerUpDown control in my UI (this control is part of the Extended WPF Toolkit from Xceed). The default value of the property that my control is bound to is 10. My user deletes the value from the control and my validation rule is triggered which results in a validation error and the UI is updated appropriately with an error adorner, etc. The value of the property that this control is mapped to hasn't been changed so it's still set to 10. Now my user clicks the "Reset default values" button which will result in the default value (10) for the property being reset. However, the value for the property is already set to 10 so the short circuit logic in my setter will return instead of setting the property value.
So now, after my user clicks "Reset default values", I am also forcing an update on my binding target like this:
this.myIntegerUpDown.GetBindingExpression(Xceed.Wpf.Toolkit.IntegerUpDown.ValueProperty).UpdateTarget();
This solves my problem but only for this particular control. Is there any easy way to do this for all of my bound controls without having to specify each one? Thanks.
OnPropertyChanged(new PropertyChangedEventArgs(string.Empty));
This is intended to imply that ALL properties on that object have changed.
Could you do one of the following?
1) Reset the DataContext - Either recreate it, or re-set the property
var context = this.DataContext;
this.DataContext = null;
this.DataContext = context;
2) Loop through all properties programmatically via reflection and manually call OnPropertyChanged with the relevant property names.
var properties = typeof(ViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(property.Name));
}
You've mentioned validation and reset values, and of course the obvious one is to persist it.
Why don't you implement IEditableObject Interface on your entity that has three signature methods. BeginEdit(), CancelEdit() and EndEdit()
That way you can easily roll back your entity to the whatever you want, or validate it and lastly persist it. A good example is found here
Sample code
public class Customer : IEditableObject
{
struct CustomerData
{
internal string id ;
internal string firstName ;
internal string lastName ;
}
private CustomersList parent;
private CustomerData custData;
private CustomerData backupData;
private bool inTxn = false;
// Implements IEditableObject
void IEditableObject.BeginEdit()
{
Console.WriteLine("Start BeginEdit");
if (!inTxn)
{
this.backupData = custData;
inTxn = true;
Console.WriteLine("BeginEdit - " + this.backupData.lastName);
}
Console.WriteLine("End BeginEdit");
}
void IEditableObject.CancelEdit()
{
Console.WriteLine("Start CancelEdit");
if (inTxn)
{
this.custData = backupData;
inTxn = false;
Console.WriteLine("CancelEdit - " + this.custData.lastName);
}
Console.WriteLine("End CancelEdit");
}
void IEditableObject.EndEdit()
{
Console.WriteLine("Start EndEdit" + this.custData.id + this.custData.lastName);
if (inTxn)
{
backupData = new CustomerData();
inTxn = false;
Console.WriteLine("Done EndEdit - " + this.custData.id + this.custData.lastName);
}
Console.WriteLine("End EndEdit");
}
public Customer(string ID) : base()
{
this.custData = new CustomerData();
this.custData.id = ID;
this.custData.firstName = "";
this.custData.lastName = "";
}
public string ID
{
get
{
return this.custData.id;
}
}
public string FirstName
{
get
{
return this.custData.firstName;
}
set
{
this.custData.firstName = value;
this.OnCustomerChanged();
}
}
public string LastName
{
get
{
return this.custData.lastName;
}
set
{
this.custData.lastName = value;
this.OnCustomerChanged();
}
}
internal CustomersList Parent
{
get
{
return parent;
}
set
{
parent = value ;
}
}
private void OnCustomerChanged()
{
if (!inTxn && Parent != null)
{
Parent.CustomerChanged(this);
}
}
public override string ToString()
{
StringWriter sb = new StringWriter();
sb.Write(this.FirstName);
sb.Write(" ");
sb.Write(this.LastName);
return sb.ToString();
}
}
Wouldn't it be easier to just always call OnPropertyChanged regardless of whether its the same? How much of a performance boost does that give you?

how to make read only property available in my usercontrol

i make user control from 3 text boxes but i don not how to declare read only property to it i tried many things but it do not work here is my code to make the control
i want to make it read only when needed like if i add checkbox i want if checkbox.check=true make my control readonly
public partial class dateIN : UserControl
{
Dates datess = new Dates();
public dateIN()
{
InitializeComponent();
}
private void dateIN_Leave(object sender, EventArgs e)
{
if (txtDay.Text != "" || txtMonth.Text != "" || txtYear.Text != "")
{
if (!datess.IsHijri(txtDay.Text.Trim() + "/" + txtMonth.Text.Trim() + "/" + txtYear.Text.Trim()))
{
txtDay.Focus();
}
}
}
public string Day
{
set { txtDay.Text = value; }
get { return txtDay.Text; }
}
public string Month
{
set { txtMonth.Text = value; }
get { return txtMonth.Text; }
}
public string Year
{
set { txtYear.Text = value; }
get { return txtYear.Text; }
}
need to know how to make read only property available here plz
just remove the set { } part of the property
Example:
public string Day
{
get { return txtDay.Text; }
}
I dont know the correlation of where your "txtDay", "txtMonth", "txtYear" come from, but you could do something like
public partial class dateIN : UserControl
{
...
...
private bool AllowEditing()
{ return SomeCondition when SHOULD be allowed...; }
public string Day
{
// only allow the set to apply the change if the "AllowEditing" condition
// is true, otherwise, ignore the attempt to assign.
set { if( AllowEditing() )
txtDay.Text = value; }
get { return txtDay.Text; }
}
// same concept for month and year too
}
so may you add some flag to your set when it is true then you set a value.
also you can work with textbox property called ReadOnly.

Programmatically WPF listbox.ItemsSource update

I am doing a program like messenger that has all the contacts in a listbox with the relative states of the contacts.
Cyclic I get a xml with the contacts were updated over time, then updates the states within a class of binding called "Contacts".
The class Contacts has a filter to display only certain contacts by their state, "online, away, busy,.. " but not offline, for example ....
Some code:
public class Contacts : ObservableCollection<ContactData>
{
private ContactData.States _state = ContactData.States.Online | ContactData.States.Busy;
public ContactData.States Filter { get { return _state; } set { _state = value; } }
public IEnumerable<ContactData> FilteredItems
{
get { return Items.Where(o => o.State == _state || (_state & o.State) == o.State).ToArray(); }
}
public Contacts()
{
XDocument doc = XDocument.Load("http://localhost/contact/xml/contactlist.php");
foreach (ContactData data in ContactData.ParseXML(doc)) Add(data);
}
}
Update part:
void StatusUpdater(object sender, EventArgs e)
{
ContactData[] contacts = ((Contacts)contactList.Resources["Contacts"]).ToArray<ContactData>();
XDocument doc = XDocument.Load("http://localhost/contact/xml/status.php");
foreach (XElement node in doc.Descendants("update"))
{
var item = contacts.Where(i => i.UserID.ToString() == node.Element("uid").Value);
ContactData[] its = item.ToArray();
if (its.Length > 0) its[0].Data["state"] = node.Element("state").Value;
}
contactList.ListBox.ItemsSource = ((Contacts)contactList.Resources["Contacts"]).FilteredItems;
}
My problem is that when ItemsSource reassigns the value of the listbox, the program lag for a few seconds, until it has finished updating contacts UI (currently 250 simulated).
How can I avoid this annoying problem?
Edit:
I tried with Thread and after with BackgroundWorker but nothing is changed...
When i call Dispatcher.Invoke lag happen.
Class ContactData
public class ContactData : INotifyPropertyChanged
{
public enum States { Offline = 1, Online = 2, Away = 4, Busy = 8 }
public event PropertyChangedEventHandler PropertyChanged;
public int UserID
{
get { return int.Parse(Data["uid"]); }
set { Data["uid"] = value.ToString(); NotifyPropertyChanged("UserID"); }
}
public States State
{
get { return (States)Enum.Parse(typeof(States), Data["state"]); }
//set { Data["state"] = value.ToString(); NotifyPropertyChanged("State"); }
//correct way to update, i forgot to notify changes of "ColorState" and "BrushState"
set
{
Data["state"] = value.ToString();
NotifyPropertyChanged("State");
NotifyPropertyChanged("ColorState");
NotifyPropertyChanged("BrushState");
}
}
public Dictionary<string, string> Data { get; set; }
public void Set(string name, string value)
{
if (Data.Keys.Contains(name)) Data[name] = value;
else Data.Add(name, value);
NotifyPropertyChanged("Data");
}
public Color ColorState { get { return UserStateToColorState(State); } }
public Brush BrushState { get { return new SolidColorBrush(ColorState); } }
public string FullName { get { return Data["name"] + ' ' + Data["surname"]; } }
public ContactData() {}
public override string ToString()
{
try { return FullName; }
catch (Exception e) { Console.WriteLine(e.Message); return base.ToString(); }
}
Color UserStateToColorState(States state)
{
switch (state)
{
case States.Online: return Colors.LightGreen;
case States.Away: return Colors.Orange;
case States.Busy: return Colors.Red;
case States.Offline: default: return Colors.Gray;
}
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public static ContactData[] ParseXML(XDocument xmlDocument)
{
var result = from entry in xmlDocument.Descendants("contact")
select new ContactData { Data = entry.Elements().ToDictionary(e => e.Name.ToString(), e => e.Value) };
return result.ToArray<ContactData>();
}
}
I developed a similar software: a huge contact list with data (presence and other stuff) updating quite frequently.
The solution I used is different: instead of updating the whole itemssource everytime, that is quite expensive, implement a ViewModel class for each contact. The ViewModel class should implement INotifiyPropertyChanged.
At this point when you parse the XML, you update the ContactViewModel properties and this will trigger the correct NotifyPropertyChanged events that will update the correct piece of UI.
It might be expensive if you update a lot of properties for a lot of contacts at the same time, for that you can implement some kind of caching like:
contactViewModel.BeginUpdate()
contactViewModel.Presence = Presence.Available;
..... other updates
contactViewModel.EndUpdate(); // at this point trigger PropertyCHanged events.
Another point:
keep a separate ObservableCollection bound to the ListBox and never change the itemssource property: you risk losing the current selection, scrollposition, etc.
dynamically add/remove elements from the collection bound to the listbox.
Buon divertimento e in bocca al lupo :-)
Move the downloading and parsing of the contact status information to another thread.
The line where you assigning the ItemsSource I would put in another thread, but remember about invoking or you're gonna have irritating errors.

Categories