How to change a property when another property is changed in MVVM - wpf

I am using WPF and MVVM for a project. I have a view with a GridView control. User can Insert/Update/Delete In Grid View. when any of the action happen changes reflect in ViewModel. This part is working Ok. But when I want to save the changes in Database I need to loop through each Item in ItemSource one by one. which takes the extra time to complete. I want to process only those Items which are changes.
To accomplish this , I add a boolean property in my Model to indicate whether the Item is changed or note. But problem is that I can not see any way to set this boolean property whenever any other property is changed.
Can any body help me how to do it?
EDIT
I have a SelectedItem Property , and I am assuming that whenever an Item is selected in GridView , User will update or insert the row. so on SelectedItem property I have set boolean property of SelectedItem to True. and while looping to save records I am saving all those records who have True in their boolean property. I know its not the perfact way, but right now I do not have any other way to do it. Your thoughts?

You could subscribe to the PropertyChanged event on your Model and set the Flag to True.
But keep in mind that you have to set the Flag to false after you loaded the data from the database, because the initialization of the model will also call the propertychanged event.
Example for class with IsDirty-Flag:
public class Sample : INotifyPropertyChanged
{
private int id;
private string name;
private bool isDirty;
public event PropertyChangedEventHandler PropertyChanged;
public int Id
{
get { return id; }
set
{
if(id != value)
{
id = value;
RaisePropertyChanged("Id");
}
}
}
public string Name
{
get { return name; }
set
{
if (name != value)
{
name = value;
RaisePropertyChanged("Name");
}
}
}
public bool IsDirty
{
get { return isDirty; }
set
{
if (isDirty != value)
{
isDirty = value;
RaisePropertyChanged("IsDirty");
}
}
}
protected virtual void RaisePropertyChanged(string propertyName)
{
if (propertyName != "IsDirty")
{
IsDirty = true;
}
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
if you are using an ObservableCollection, you can also add an eventhandler to track the rows that are newly added or deleted

If you use MVVM you should have implementation of INotifyPropertyChanged. And you can add some logic to set up yours boolean property in OnPropertyChanged handler.

If you are willing to take a dependency on a build time tool you can do this with ILWeaving.
So if you combine Fody with the PropertyChanged addin then IsDirty functionality is supported out of the box.
Then Martins example can be simplified to this
public class Sample : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int Id { get; set; }
public string Name { get; set; }
public bool IsChanged { get; set; }
}
Note the use if IsChanged instead of IsDirty.
And then this will exist in the compiled assembly
public class Sample : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
int id;
public int Id
{
get { return id; }
set
{
if (id != value)
{
id = value;
IsChanged = true;
OnPropertyChanged("Id");
}
}
}
bool isChanged;
public bool IsChanged
{
get { return isChanged; }
set
{
if (isChanged != value)
{
isChanged = value;
OnPropertyChanged("IsChanged");
}
}
}
string name;
public string Name
{
get { return name; }
set
{
if (!string.Equals(name, value, StringComparison.Ordinal))
{
name = value;
IsChanged = true;
OnPropertyChanged("Name");
}
}
}
}

Related

Tick the checkbox column in WPF datagrid through code

I have an ObservableCollection of items in which one of the property is bool.
When i set the itemsSource of the datagrid as the ObservableCollection, it auto-generates the column with checkbox column for the bool property.
I would like to know how we can tick the checkbox in code, lets say if we have the mark all option?
I tried updating the ObservableCollection records property value with true, but it doesnt help updating the UI.
Please help.
[EDIT: Below code works as suggested in the answer]
My Class is as follows
public class InvoiceDoc : INotifyPropertyChanged
{
private bool _Selected;
[DisplayName("Selected")]
public bool Selected
{
get { return _Selected; }
set { _Selected = value; this.OnPropertyChanged(); }
}
[DisplayName("Date")]
public DateTime DocDate { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged !=null)
this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The datagrid is as follows
<DataGrid x:Name="dgInvoices" Margin="32,110,32,59" AutoGeneratingColumn="dgInvoices_AutoGeneratingColumn"/>
setting the ItemsSource is as follows
docs = new ObservableCollection<InvoiceDoc>(); ;
dgInvoices.ItemsSource = docs;
I am expecting the grid to auto check the check box once is set the value in the collection.
Binding to an ObservableCollection is only reactive if an Item is added or removed.
Your elements inside your Collection have to implement INotifyPropertyChanged so the UI recognises the changes
EDIT:
Lets say you have the following objects in your Collection:
public class MyClass {
public string Name { get; set; }
public bool IsActive { get; set; }
}
This class has now to be modified to the following:
public class MyClass : INotifyPropertyChanged{
private string _name;
private bool _isActive;
public string Name
{
get { return this._name; }
set { this._name = value; this.OnPropertyChanged();}
}
public bool IsActive
{
get { return this._isActive; }
set { this._isActive = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If there are any Errors, remove the CallerMemberNameAttribute and invoke the this.OnPropertyChanged(); with the Propertyname.

How to Update Model from the View

I have Model Class Employee:
public class Employee
{
public string Id { get; set; }
public string Name { get; set; }
public string LastName { get; set; }
}
and View Model class EmployeeViewModel which contains an Employee Object
public class EmployeeViewModel : INotifyPropertyChanged
{
public EmployeeViewModel()
{
}
private Employee currentEmployee;
public Employee CurrentEmployee
{
get { return this.currentEmployee; }
set
{
this.currentEmployee = value;
this.NotifyPropertyChanged("CurrentEmployee");
}
}
//Some code .....
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
Now the View (WPF) will use Employee object in the View Model as ItemSource to display Employee data
Now the question is: I have Update button on the view and when I change the Employee properties in the view (via text boxes) I want to update the model (so afterward i can update the database), how to update this model from the view.
As I checked there something weird with your Model Class. It is the one that should implement INotifyPropertyChanged then create backing fields for each property something like this.
Model Class
public class Employee:INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name= value;
OnPropertyChanged("Name");
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
**ViewModel Class**
class EmployeeViewModel
{
private IList<Employee> _employees;
public EmployeeViewModel()
{
_employees= new List<Employee>
{
new Employee{ID=1, Name ="Emp1"},
new Employee{ID=2, Name="Emp2"}
};
}
public IList<Employee> Employees
{
get
{
return _employees;
}
set
{
_employees= value;
}
}
private ICommand mUpdater;
public ICommand UpdateCommand
{
get
{
if (mUpdater == null)
mUpdater = new Updater();
return mUpdater;
}
set
{
mUpdater = value;
}
}
private class Updater : ICommand
{
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
}
#endregion
}
}
You may put your logic inside OnPropertyChanged event. This method is always called whenever you made changes on the UI
If you are using ObservableCollection modify your List by searching the item based on ID then if found do the modification of values. Whatever changes you have made will automatically affect the UI items if they are bound to your ObservableCollection then use the modified collection to update your DB records

Partial class entity framework propertychanged

With database first scenario, I have a many - many relation and use EF as ORM & DAL:
Customer: ID, Name, Address ||
Product: ID, Name ||
CustomerProduct: CutomerID, ProductID
I add a custom property to the Product entity class , called Isincludedforcustomer.
public partial class Product: EntityObject
{
public bool isincludedforcustomer;
public bool Isincludedforcustomer
{
get { return isincludedforcustomer; }
set {isincludedforcustomer= value; }
}
When a Customer is selected, I have a method to assign the new property.
IsProductinclinframe(Displayedcustomerproducts, products);
How can i implement property changed to this property ?
I generally put a call to the PropertyChanged event in the setter of the property.
public partial class Product: EntityObject, INotifyPropertyChanged
{
public bool isincludedforcustomer;
public bool Isincludedforcustomer
{
get { return isincludedforcustomer; }
set
{
isincludedforcustomer= value;
RaisePropertyChanged("Isincludedforcustomer");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}

MVVM Binding to properties of an observable property

I am trying to figure out on how to trigger the PropertyChangedEvent when the middle layer of my binding changes. I will start with an example here:
public class MainViewModel :NotificationObject // Main DataContext
{
public SubViewModel SubVM{get; {_subVM = value; RaisePropertyChanged("SubVM");}} // observable property
public void DoChangeSubVM()
{
SubVM = new SubViewModel(); // doing this will not update the egControl
}
}
public class SubViewModel : NotificationObject
{
public Sub2ViewModel Sub2VM {get; set{_sub2VM = value; RaisePropertyChanged("Sub2VM");}} // observable property
}
public class Sub2ViewModel : NotificationObject
{
public int SomeProp {get; set {_someProp = value; RaisePropertyChanged("SomeProp");} // observable property
}
in the XAML:
<EgControl name="egControl" Content={Binding SubVM.Sub2VM.SomeProp} />
Now if I change the Sub2VM Property the egControl doesn't automagically get updated with the SomeProp value of the new Sub2VM instance. How does someone go about achieving this, with out manually having to raise all the Sub2ViewModel propertychanged events from Sub2VM property setter?
Using: Prism .NET 4.0
How does someone go about achieving this, with out manually having to raise all the Sub2ViewModel propertychanged events from Sub2VM property setter?
Answer
You have several possibilities:
Raise all property changed events in the setter, which you said you wanted to avoid. But it's a valid strategy to consider. If you know which properties are dependant on the results of another, then they will need to raise property changed in the setter for many properties.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
}
Raise all property changed events in the method, after the new ViewModel has been constructed.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
RaisePropertyChanged("FirstName");
}
}
public void UpdateFirstName(string firstName)
{
_FirstName = firstName;
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
Use the setters to set some properties, thus triggering the already present property changed event.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
RaisePropertyChanged("FirstName");
}
}
public Person ClonePerson(Person rootPerson)
{
Person clone = new Person()
{
FirstName = rootPerson.FirstName;
LastName = rootPerson.LastName;
}
return clone;
}
}
Make a method that raises all property changed events, and call it in edge cases where you need to raise multiple changed events.
public class myViewModel
{
private string _FirstName
public string FirstName
{
get { return_FirstName };
set
{
_FirstName = value;
this.RaiseAllPropertyChanges();
}
}
public void RaiseAllPropertyChanges()
{
RaisePropertyChanged("FirstName");
RaisePropertyChanged("FullName");
}
}
The end result is this: For any bound UI element to know that it must update, the property changed event for that property must be raised.
Okay, not sure about Prism, but generally speaking all three classes should be implementing property change notification. The simplist way to do so is with INotifyPropertyChanged. So SubViewModel should be more like:
public class SubViewModel : INotifyPropertyChanged
{
private Sub2ViewModel sub2vm;
public Sub2ViewModel Sub2VM
{
get
{
return sub2vm;
}
set
{
sub2vm = value;
OnPropertyChanged("Sub2VM");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Without property change notification, the UI doesn't know when to udpate a bound property.
One way to go about it is to create a constructor for both the SubViewModel and Sub2ViewModel that initializes all the properties to some default value. This will ensure that your properties are initialized and give you the ability to set the initial values.

Observable collection collection changed question

I am learning about observable collections, so I wrote a small program to test my progress.
I have a observable collection class that I supply initial values from a list and bind the observablecollection to a Datagrid. It works great, but when I clear the list using myListOfPlayers.myList.Clear(), the Datagrid does not clear. I thought that the INotifyPropertyChanged property would handle that. What am I doing wrong?
public class PlayerList : ObservableCollection<PlayerName> //observable collection class
{
public PlayerList()
: base()
{
Clear();
foreach (var p in myListOfPlayers.myList.OrderBy(x => x.Score))
{
Add(p);
}
}
public PlayerList(List<PlayerName> list)
: base(list)
{
Clear();
foreach (var p in list)
{
Add(p);
}
}
}
I implement INotifyPropertyChanged in the PlayerName class:
public class PlayerName : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private int _score;
public int Score
{
get { return _score; }
set
{
_score = value;
OnPropertyChanged("Score");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
If you clear myListOfPlayers.myList, the PlayerList is not supposed to be cleared... There is no relation between these 2 lists: you just used myListOfPlayers.myList to initialize the content of PlayerList, but they are two different lists. What you do on one of them doesn't affect the other

Resources