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.
Related
My VS2015 solution consists of two projects: DataModel and DesktopClient.
DataModel has a Customer class - thats an EntityFramework 6 DB entity. Customer has a FirstName property.
In DesktopClient there is an extended class CustomerExt.
In DesktopClient, is it possible to have a notification to CustomerExt.FirstName changes? Defining a partial Customer across two projects won't work - DataModel is compiled first and it won't have partial properties defined in DesktopClient.
public class CustomerExt : Customer, INotifyPropertyChanged
{
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
}
}
Unfortunately, if your base class does not implement INotifyPropertyChanged the safest way is to just write a wrapper class and only use that in your software. You can fit this in with your CustExt, or make it separate if you feel you want the extra layer.
This also assumes that while you may not control the Customer class, you control all of the code creating/editing the Customer instances, so that you can use this new class instead, then convert it to the original Customer class only when needed (such as a database transaction).
public class CustomerExt: INotifyPropertyChanged
{
Customer _customer = new Customer();
public object Clone()
{
return this.MemberwiseClone();
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
this._isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
#region WrapperProperties
public bool FirstName
{
get { return _customer.FirstName; }
set
{
_customer.FirstName= value;
NotifyPropertyChanged("FirstName");
}
}
#endregion
public Customer ToCustomer()
{
// returning a copy of the _customer instance here is safer than returning
// the reference, otherwise the properties could be altered directly
}
#region INotifyPropertyChanged
...
}
Some of this gets a little easier if you have an ICustomer interface and that is used during the database calls, then you can skip the formality of retaining a Customer instance.
I remember there being some third party libraries that have tried to automate this process - but I have never tried them and/or didn't trust them to work properly.
Let me see if I understand, you want update the View when your date is updated on the database?
You have to find a way to request this information from your ViewModel.
some kind of RefreshFirstNameAsync
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
this._firstName= value;
NotifyPropertyChanged("FirstName"); // There is better ways to implement that line
}
}
private void RefreshFirstName(){
FirstName = _userRepo.GetFirstNameAsync();
}
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.
WPF-MVVM beginner here.
My problem: in a WPF-MVVM UI I am editing an entity. Some properties when changed, require automatic updates on other properties. These are done in Entity class, set methods, but not reflected in my View
More details:
1) I have the Model (a simple class with properties) in a separate assembly (not WPF related since is the general business model). Note that "SomeOption" when set to false, requires some other options to automatically be changed.
Example:
public class Employee : BaseEntity
{
public string EmployeeNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
....
private bool someOption
public bool SomeOption {
get
{ return someOption}
set {
someOption= value;
if (!value)
{
OtherOption = false;
OtherProperty= "";
AndAnotherOption= false;
}
}
}
}
2) The WPF UI has a base ViewModel implementing INotifyPropertyChanged. The current edited record (Employee) is a public property of the ViewModel:
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
if (_selectedEmployee != value)
{
_selectedEmployee = value;
OnPropertyChanged(nameof(SelectedEmployee));
}
}
}
3) When un-checking the checkbox bound to "SomeOption", the other properties which are changed in entity code, are not reflected on the View, and stay on the screen as edited by user.
Please let me know what I am missing. Thanks!
You should implement INotifyPropertyChanged in your model to update entities at your UI. For example:
public class Employee : BaseEntity, INotifyPropertyChanged
{
private string employeeNumber;
public string EmployeeNumber {
get{return employeeNumber};
set
{
employeeNumber=value;
OnPropertyChanged("EmployeeNumber");
}
//...Other properties...
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Employee needs to implement INotifyPropertyChanged just as your viewmodel does, and fire PropertyChanged on changes to its own properties (the ones you're calling OtherOption, OtherProperty, etc.)
What you've got now will update the UI when the view model selects a different Employee, but subsequent changes to that Employee don't send any notifications.
Information for the question:
I am trying to understand how to properly implement INotifyPropertyChanged on objects and collections.
First, here is my ViewModelBase class:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertychanged([CallerMemberName] string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Consider that I have a class called Person:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Age { get; set; }
}
To use INotifyPropertyChanged, most examples that I have seen change the Person class to something like this:
public class Person
{
public int Id { get; set; }
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertychanged();
}
}
private string _age;
public string Age
{
get { return _age; }
set
{
_age = value;
OnPropertychanged();
}
}
}
It seems to work exactly the same when used a single time on an instance of the object (This might be useful if there are a lot of properties):
private Person _person;
public Person MyPerson
{
get { return _person; }
set
{
_person = value;
OnPropertychanged();
}
}
Actual question:
1 - Does it make a difference (aside from amounts of code) whether you call OnPropertychanged() on each individual property verses on an instance of an object? (Are both considered good practice?)
2 - If setting OnPropertychanged() on the object instance is good practice, am I correct to create an ObservableCollection like this?:
var PersonCollection = new ObservableCollection<MyPerson>();
1) Well, if you want to call it on object instance, then you need to do it every time you use your class like this in binding. When you implement OnNotifyPropertyChanged directly inside your class, you don't need to care about it later on...
2) Classes with INotifyPropertyChanged do not require Observable collections. This is however must when you are binding colection do some UI control (ListBox, ListView) and want to add/remove its elements. Observable collection will then make sure the UI gets updated.
The ObservableCollections object... When adding and removing from this collection the UI will be notified of the changes (Top Level). If you have an "ObservableCollection of Person" and you change a property on the one of the objects(Person) in the list the UI will not update unless your "Person" class implements the INotifyPropertyChanged interface, which can be put into a base class that all classes can inherit from like your example. I hope this helps a little.
i have a model user:
public class User
{
public string Name { get; set; }
public int Level { get; set; }
}
in the view:
<TextBox Text="{Binding NewUser.Name}"/>
<TextBox Text="{Binding NewUser.Level}"/>
and the property in the VM:
public User NewUser
{
get { return _newUser; }
set
{
if (_newUser == value)
return;
_newUser = value;
RaisePropertyChanged("NewUser");
}
}
this code does update the property:
NewUser = new User() { Name = "test", Level = 1 };
this code does not:
NewUser.Name = "test";
what am i doing wrong? i'm using mvvm light.
When setting NewUser.Name, the RaisePropertyChanged on the ViewModel is not called and therefore no PropertyChangedEvent is fired.
In general you should have a good reason to expose model classes directly in your ViewModel, as you do here (Expose a User model as a public property in your ViewModel). This basically violates the separation of concerns between Models and ViewModels, for which MVVM is designed. Though it seems academic, my experience is that it is really worth it to stay clean here, as in most real-world cases the ViewModels tend to become more complex over time and contain functionality that you don't want to have in your model (like INPC implementations, btw).
Although it involves a bit more coding, you should implement a nested ViewModel here. Here's a bit of code to get you started:
public class ParentViewModel : NotifyingObject
{
private UserViewModel _user;
// This is the property to bind to
public UserViewModel User
{
get { return _user; }
private set
{
_user = value;
RaisePropertyChanged(() => User);
}
}
public ParentViewModel()
{
// Wrap the new instance in a ViewModel
var newUser = new User {Name = "Test"};
User = new UserViewModel(newUser);
}
}
This is the extra ViewModel in which the User model class is wrapped:
public class UserViewModel : NotifyingObject
{
/// <summary>
/// The model is private here and not exposed to the view
/// </summary>
private readonly User _model;
public string Name
{
get { return _model.Name; }
set
{
_model.Name = value;
RaisePropertyChanged(() => Name);
}
}
public UserViewModel(User model)
{
_model = model;
}
}
This is your model class. There is no need to implement INotifyPropertyChanged.
public class User
{
public string Name { get; set; }
}
You did not implement INotifyPropertyChanged for your User class. So changing the property NewUser by assignment will trigger the UI, setting the property Name by assignment will not.
If you follow your pattern, this:
public string Name { get; set; }
should in the end look like this:
public string Name
{
get { return _name; }
set
{
if (_name == value)
return;
_name = value;
RaisePropertyChanged("Name");
}
}