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();
}
Related
I have a problem with MVVM pattern and binding collection. My ViewModel provides a collection to the View but to get this collection I use this:
public BindingList<Car> BindingListCars { get; set; }
public CarsVm()
{
BindingListVoiture = carServices.ListCars;
}
When I bind my View on this List it's as if I bind directly my View on the Model because they use the same reference. So when I edit one property of a Car, the model is directly edited without using carServices validation method.
What is the best solution to correct this problem ?
Do I have to expose a copy of my Model to my View to not edit directly my Model from the View?
Do I have to use BindingList in my Model and subsribe to ListChanged in my carServices to validate each change?
You should either perform the validation directly in the Car class itself or expose wrapper objects instead of exposing the "real" Car objects to the view.
The following sample code should give you the idea about what I mean:
//the "pure" model class:
public class Car
{
public string Model { get; set; }
}
public class CarService
{
public List<CarWrapper> ListCar()
{
List<Car> cars = new List<Car>(); //get your Car objects...
return cars.Select(c => new CarWrapper(c, this)).ToList();
}
public bool Validate()
{
//
return true;
}
}
public class CarWrapper
{
private readonly Car _model;
CarService _service;
public CarWrapper(Car model, CarService service)
{
_model = model;
_service = service;
}
//create a wrapper property for each property of the Car model:
public string Model
{
get { return _model.Model; }
set
{
if(_service.Validate())
_model.Model = value;
}
}
}
Obviously if you expose an IEnumerable<Car> from your view model for the view to bind, you are effectively bypassing any validation that is dedined outside of the Car class if the view is able to set any properties of the Car class.
Thanks for your answer mm8,
With this solution I have to create one wrapper per class which need outside validation. It add work and during refactoring we have to edit the class and the Wrapper.
What do you think about this solution :
I put my list of vehicle in a binding list
My service subscribe to ListChanged event of this list
My service implement INotifyDataErrorInfo
For each modification in this list validation is executed
If there is an error ErrorsChanged event is raised
The view model subsribe to this event and retrieve error Data.
The view model subsribe to this event and retrieve error Data.
For example :
My services implementation :
public class VehicleServices : INotifyDataErrorInfo
{
private BindingList<Vehicle> _bindingListCar
public BindingList<Vehicle> BindingListCar
{
get return _bindingListCar;
}
private readonly Dictionary<string, ICollection<string>>
_validationErrors = new Dictionary<string, ICollection<string>>();
//INotifyDataErrorInfo implementation
public IEnumerable GetErrors(string propertyName)
public bool HasErrors
private void RaiseErrorsChanged(string propertyName)
public VehicleServices()
{
_bindingListCar = GetVehicles();
_bindingListCar.ListChanged += BindingListVehicleChanged;
}
private void BindingListVehicleChanged(object sender, ListChangedEventArgs e)
{
//Only modification is managed
if (e.ListChangedType != ListChangedType.ItemChanged) return;
switch(e.PropertyDescriptor.Name)
//Validate each property
//if there is ErrorsChanged is raised
}
}
And my ViewModel
public class CarVm : BindableBase
{
private ICollection<string> _errors;
public ICollection<string> Error
{
get
{
return _errors;
}
set
{
SetProperty(ref _errors, value);
}
}
private VehicleServices _carServices;
public BindingList<Vehicle> BindingListCar { get; set; }
public CarVm(VehicleServices carServices)
{
_carServices = carServices;
BindingListCar = new BindingList<Vehicle>(_carServices.BindingListCar);
_carServices.ErrorsChanged += _carServices_ErrorsChanged;
}
private void _carServices_ErrorsChanged(object sender, DataErrorsChangedEventArgs e)
{
Error = _carServices.ValidationErrors[e.PropertyName];
}
}
Do you think this is a good practice ?
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'm trying to work out an issue I'm having with implementing MVVM in WPF. My Contact class below is my model that's being populated by Entity Framework.
public class Contact : INotifyPropertyChanged
{
public string _firstName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public string _lastName;
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
//INotifyPropertyChanged implementation omitted for brevity
}
Here's my ViewModel:
public class ContactViewModel
{
public Contact MyContact { get; set; }
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
}
So I set my View's datasource to an instance of ContactViewModel, and I'm binding two TextBoxes to MyContact.FirstName and MyContact.LastName. I'm binding a TextBlock to FullName. When I change either of my TextBoxes the Full Name TextBlock doesn't update (obviously, I'm not doing an OnPropertyChanged("FullName") anywhere).
The question is, where do I add OnPropertyChanged("FullName")? I don't necessarily want to modify my model because it's being used elsewhere and I don't to tie it to my ViewModel.
Do I need to rethink my architecture?
Do I need to rethink my architecture?
This can be solved with your current architecture. You just need to propagate the call from your Contact object to your viewModel object.
You will need to implement INotifyPropertyChanged in the viewModel to achieve this.
Something like this:
public class ContactViewModel : INotifyPropertyChanged
{
//INotifyPropertyChanged implementation omitted for brevity...
private Contact _myContact;
public Contact MyContact
{
get
{
return _myContact;
}
set
{
_myContact.PropertyChanged -= myHandler;
_myContact = value;
_myContact.PropertyChanged += myHandler;
}
}
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
private void myHandler(Object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("FullName");
}
}
I would also recommend taking a look at MVVM Foundation as this includes a class called PropertyObserver which is designed to make wiring up this sort of thing much easier.
If you want to take the more MVVM pure approach suggested by Big Daddy, you would need to do something like this:
public class ContactViewModel : INotifyPropertyChanged
{
// INotifyPropertyChanged implementation omitted for brevity...
// You will require some way of setting this, either via a property
// or the viewModel constructor...
private Contact _myContact;
public string FirstName
{
get { return _myContact.FirstName; }
set
{
_myContact.FirstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
public string LastName
{
get { return _myContact.LastName; }
set
{
_myContact.LastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
public string FullName
{
get
{
return MyContact.FirstName + " " + MyContact.LastName;
}
}
}
Do I need to rethink my architecture?
Maybe...
It looks to me like you're binding your view's properties to your view-model (ContactViewModel) and your model (Contact). Your view can see your public model's properties, etc. via your view-model - I don't think this is good. It looks like a violation of the Law of Demeter. I'd rather see you use your view-model as a wrapper/façade to your model. This creates more work for sure, but I think it gives you a better design and more flexibility. Your view-model will need to implement INotifyPropertyChanged for this to work.
Say I have the following class:
public MainFormViewModel
{
public String StatusText {get; set;}
}
What is the easiest smallest way to get my changes to StatusText to reflect to any controls that bind to it?
Obviously I need to use INotifyPropertyChanged, but is there a cool way to do it that does not clutter up my code? need lots of files? etc?
Note: If this is a dupe then I am sorry. I searched and could not find any thing but using T4 code Generation which does not sound easy (to setup at least).
Unfortunately C# doesn't offer an easy mechanism to do that automatically... It has been suggested to create a new syntax like this :
public observable int Foo { get; set; }
But I doubt it will ever be included in the language...
A possible solution would to use an AOP framework like Postsharp, that way you just need to decorate your properties with an attribute:
public MainFormViewModel : INotifyPropertyChanged
{
[NotifyPropertyChanged]
public String StatusText {get; set;}
}
(haven't tried, but I'm pretty sure Postsharp allows you to do that kind of thing...)
UPDATE: OK, I managed to make it work. Note that it's a very crude implementation, using reflection on a private field to retrieve the delegate... It could certainly be improved, but I'll leave it to you ;)
[Serializable]
public class NotifyPropertyChangedAttribute : LocationInterceptionAspect
{
public override void OnSetValue(LocationInterceptionArgs args)
{
object oldValue = args.GetCurrentValue();
object newValue = args.Value;
base.OnSetValue(args);
if (args.Instance is INotifyPropertyChanged)
{
if (!Equals(oldValue, newValue))
{
RaisePropertyChanged(args.Instance, args.LocationName);
}
}
}
private void RaisePropertyChanged(object instance, string propertyName)
{
PropertyChangedEventHandler handler = GetPropertyChangedHandler(instance);
if (handler != null)
handler(instance, new PropertyChangedEventArgs(propertyName));
}
private PropertyChangedEventHandler GetPropertyChangedHandler(object instance)
{
Type type = instance.GetType().GetEvent("PropertyChanged").DeclaringType;
FieldInfo propertyChanged = type.GetField("PropertyChanged",
BindingFlags.Instance | BindingFlags.NonPublic);
if (propertyChanged != null)
return propertyChanged.GetValue(instance) as PropertyChangedEventHandler;
return null;
}
}
Note that your class still need to implement the INotifyPropertyChanged interface. You just don't have to explicitly raise the event in your property setters.
Have a go of this http://code.google.com/p/notifypropertyweaver/
All you need to do is implement INotifyPropertyChanged
So your code will look like
public MainFormViewModel : INotifyPropertyChanged
{
public String StatusText {get; set;}
#region INotifyPropertyChanged Implementation
}
The build task will compile this (you never see the below code)
public MainFormViewModel : INotifyPropertyChanged
{
public String StatusText {get; set;}
private string statusText;
public string StatusText
{
get { return statusText; }
set
{
if (value!= statusText)
{
statusText = value;
OnPropertyChanged("StatusText");
}
}
}
#region INotifyPropertyChanged Implementation
}
By leveraging EqualityComparer.Default you can reduce the property setter code down to one line as follows:
private int unitsInStock;
public int UnitsInStock
{
get { return unitsInStock; }
set { SetProperty(ref unitsInStock, value, "UnitsInStock"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T field, T value, string name)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
If your view models inherit from a base class that defines the SetProperty method and the PropertyChanged event, then the amount of code required to support INotifyPropertyChanged in your child view models becomes very minimal (1 line).
This approach is more verbose then the code weaving methods mentioned in other answers, but doesn't require you to modify your build process to accomplish it.
Be sure to take a look at the upcoming C# 5 Caller Info attributes as well as it looks like they will allow us to avoid using a magic string in the method without the performance cost of reflection.
UPDATE (March 1st, 2012):
The .NET 4.5 Beta is out, and with it, you can further refine the above code to this which removes the need for the string literal in the caller:
private int unitsInStock;
public int UnitsInStock
{
get { return unitsInStock; }
set
{
SetProperty(ref unitsInStock, value);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
I have a blog post that talks about it in slightly more detail.
Ive always liked this method
private string m_myString;
public string MyString
{
get { return m_myString; }
set
{
if (m_myString != value)
{
m_myString = value;
NotifyPropertyChanged("MyString");
}
}
}
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
or for less code bloat
set
{
m_myString = value;
NotifyPropertyChanged("MyString");
}
I have a base class called "Model". It exposes a protected object called DataPoints, which is essentially a dictionary.
C#
public String StatusText {
get {
return (string)DataPoints["StatusText"];
}
set {
DataPoints["StatusText"] = value;
}
}
VB
public Property StatusText as String
get
return DataPoints!StatusText
end get
set
DataPoints!StatusText = value
end set
end property
When you set a value in the DataPoints dictionary it does the following:
Checks to make sure the value actually changed.
Saves the new value
Sets the IsDirty property to true.
Raises the Property Changed event for the named property as well as the IsDirty and IsValid properties.
Since it is a dictionary, it also makes loading objects from a database or XML file really easy.
Now you may think reading and writing to dictionary is expensive, but I've been doing a lot of performance testing and I haven't found any noticable impact from this in my WPF applications.
The PropertyChanged.Fody NuGet package does this.
https://github.com/Fody/PropertyChanged
Add the PropertyChanged.Fody package to your project.
Reference PropertyChanged in your model: using PropertyChanged;
Add the [ImplementPropertyChanged] attribute to your class.
All of the properties in the class will now magically implement INotifyPropertyChanged. Note - Fody works by modifying the emitted IL so you will never actually see the code in VS - it just magically does it.
Additional docs:
https://github.com/Fody/PropertyChanged/wiki/Attributes