I am trying to implement INotifyPropertyChanged for a lot of classes, and each of these classes have lots and lots of properties. I have been following this MSDN documentation for how to implement INofifyPropertyChanged, but their instructions don't seem to be practical in cases where a class has many many properties.
Currently most of my properties use the short hand:
public DateTime? DateClosed { get; set; }
But the documentation says that i need to add the following to each setter method:
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("DateClosed");
This means that I then need to declare a body for the get method and declare private variables to handle the actual getting and setting of properties. Like this:
private DateTime? _dateOfIncident = null;
public DateTime? DateClosed
{
get { return _dateOfIncident; }
set
{
_dateOfIncident= value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("DateClosed");
}
}
Does anyone know a way around this?
A few classes can easily be changed to implement INotifyPropertyChanged. But since you state you have a lot of classes with a lot of properties, it's a real burden to get this done manually or even with templates.
What you really need is a tool that does it for you, so I present you Fody and it's NotifyPropertyChanged plugin. What Fody does is weave some extra code in between your code at compile time. The only thing you have to do is add a single attribute on the classes you want to implement INotifyPropertyChanged and the rest is done for you.
[ImplementPropertyChanged]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
}
I'm not sure you're going to find a workaround here. Auto-properties, as you're using them now, are really just a compiler shorthand that get's converted to full properties with a backing field eventually anyway (at least, as I understand it).
The use of INPC is a routine that's sorta separate and apart from the duty of a normal property. It's notifying subscribers (usually, your view XAML) that the property in question has changed or is changing.
tl;dr -- you're not going to get around having to rewrite autoproperties to full properties with backing fields. But toolkits like MVVMLight have some great Visual Studio code snippets to make this relatively fast. Eventually you can even do this:
private string _someString;
public string SomeString
{
get { return _someString;}
set
{
//Set returns bool, so you can trigger other logic on it!
Set(() => SomeString, ref _someString, value);
}
}
This gives you some neat features:
Strong naming (unlike the magic string in your example)
Set only triggers INPC event if the value is different
Set returns boolean so you can perform more action if the value changed
MVVMLight is nice in that you don't have to use all its features, or even implement MVVM pattern. It just has a lot of nice 'tools' you can leverage.
There are a lot of patterns to do it, or you can buy a tool like PostSharp that will do it for you.
For example, here is one method of doing it:
public abstract class BaseNotifyPropertyChanged : INotifyPropertyChanged
{
private Dictionary<string, object> _valueStore = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
protected T Get<T>([CallerMemberName]string property = null)
{
object value = null;
if (!_valueStore.TryGetValue(property, out value))
return default(T);
return (T)value;
}
protected void Set<T>(T value, [CallerMemberName]string property = null)
{
_valueStore[property] = value;
OnPropertyChangedInternal(property);
}
protected void OnPropertyChanged([CallerMemberName]string property = null)
{
OnPropertyChangedInternal(property);
}
private void OnPropertyChangedInternal(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Which you then inherit from your classes:
public class PlainOldObject : BaseNotifyPropertyChanged
{
public int MyProperty
{
get { return Get<int>(); }
set { Set(value); }
}
}
Which takes care of the backing store and everything for you. You may want to add logic to only call the OnPropertyChangedInternal if the property actually changed (compare references or value), but I'll leave that as an exercise for you.
Simply use the Observable Object class. Instead of creating a DateTime property, you'd create an ObservableObject<DateTime> and you would just bind to DateClosed.Value.
Related
I have been using the Breeze.Sharp.BaseEntity base class at work for some time and up until now, have been ok with using their GetValue and SetValue properties for plugging into the INotifyPropertyChanged interface. However, occasionally, it is advantageous to notify of changes to multiple properties at once from a single property. Let me give you a simple example.
Let's say that we have a simple sum to perform. We have an ItemAmount property, an ItemQuantity property, and a Total property. Each time either of the ItemAmount or ItemQuantity property values change, we also want the total to update. Traditionally, we could do that like this:
public double ItemAmount
{
get { return _itemAmount; }
set
{
_itemAmount = value;
NotifyPropertyChanged("ItemAmount");
NotifyPropertyChanged("Total");
}
}
public int ItemQuantity
{
get { return _itemQuantity; }
set
{
_itemQuantity = value;
NotifyPropertyChanged("ItemQuantity");
NotifyPropertyChanged("Total");
}
}
public double { get => ItemAmount * ItemQuantity; }
This would make the Framework also update the value of the Total property as either property changed, as required. However, I can find no such way to call the INotifyPropertyChanged interface using their base class, as unbelievably, they seem to have hidden access to their implementation of it.
So, my question is How can I manually notify the Framework of property changes when using the Breeze.Sharp.BaseEntity class?
Is there some way to connect to their implementation that I haven't worked out yet? When I added my own implementation, it didn't seem to connect with the Framework and did not notify of changes successfully. Many thanks in advance.
There's no simple answer, but the way that I found to do this, was to clone the Breeze.Sharp repository and to make the following changes. In the Breeze.Sharp.Standard solution, open the EntityAspect.cs class and simply add your desired method to raise the PropertyChanged event handler, that is already declared in that class:
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Then, you can call it from your BaseEntity class like this:
public int ItemQuantity
{
get => GetValue<int>();
set
{
SetValue(value);
EntityAspect.NotifyPropertyChanged(nameof(Total));
}
}
Note that changes to the ItemQuantity property are raised inside the SetValue method.
Consider a WPF dialogue with lots of input fields, which are bound to properties in a view-model. E.g.
...
<TextBox Text="{Binding FirstName}">
...
public string FirstName {
get { return mFirstName; }
set {
if (mFirstName == value) return;
mFirstName = value;
OnPropertyChanged("FirstName");
}
}
As there are tens of fields like this, I would like to minimize the boilerplate C# code to be written. What options do I have?
If you have the option of using a base class, consider inheriting view model objects from something like this:
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return false;
}
storage = value;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertiesChanged(params string[] propertyNames)
{
foreach (string propertyName in propertyNames)
{
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
}
}
}
Example usage, showing that the boilerplate is greatly reduced:
public sealed class ViewModel : BindableBase
{
private string name;
public string Name
{
get { return name; }
private set { SetProperty(ref name, value); }
}
}
(If you can't use a base class (e.g., you already have one or are using properties on framework elements), you still have the option of adding similar support directly in the class in question.)
I can make your code a little easier to transform into a snippet.
if (mFirstName != value) {
mFirstName = value;
OnPropertyChanged("FirstName");
}
If just the time taken to write it is a pain, and you're using WPF a lot, snippets may also be of use. I know in Sublime Text, VS Code, and Visual Studio, Snippets can be invaluable. Otherwise, I think it's as bare bones as you can get, unless there's something I am not seeing
I use Fody to inject property changed code at compile time. Your class gets an [ImplementPropertyChanged] attribute, then your { get; set; } properties become notifying properties in the compiled code.
https://github.com/Fody/PropertyChanged
First, as I guess you already use Microsoft.Prism, you can drop the string and profit from CallerMemberNameAttribute behind the scenes for you, so that your code would look like this:
public string FirstName {
get { return mFirstName; }
set {
if (mFirstName == value) return;
mFirstName = value;
OnPropertyChanged();
}
}
This is also equivalent to c# 6.0 nameof(FirstName) operator.
Second, you can dig into AOP and abstract the boilerplate to an attribute. One of the AOP frameworks that deals with this is PostSharp and using it your code could look like this:
[NotifyPropertyChanged]
public class Customer
{
public string FirstName { get; set; }
Though it's not free, and AOP has it's drawbacks (thanks Evk).
Similar questions have been asked 1,2, and there does not seem to be optimal answer right now sadly, as it's everyones pain.
To implement data binding in WPF, according to MS a "class needs to provide the proper property changed notifications." [ref here]
AFAIK, part of setting this up means taking the following steps, if not already set up in the class (ref this article on MSDN):
When properties are changed, they need call to a method to raise an event.
This means auto-implemented properties must be changed so they use a private backing field, so set can change the property and also call the method to raise an event.
The class needs to implement INotifyPropertyChanged.
The class needs to declare the PropertyChangedEventHandler event.
The event needs to be raised in something like this:
...
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
If I already have many existing classes that do not take any of these steps, what is the best way to change these classes so they change only so far as is needed to make them meet these standards?
If you want the minimum-possible-code-impact solution, then you want the Fody PropertyChanged weaver. This is installed as a NuGet package with Install-Package PropertyChanged.Fody, or through the VS package manager dialogue.
Once installed, you can mark a class with the [ImplementPropertyChanged] attribute, and your job is basically done. The weaver will add the relevant event calls at compile time by manipulating the generated IL, which means that you don't have to explicitly implement the interface or the event calls in code. You can keep your auto-implemented property syntax, too!
The linked docs provide details of more advanced cases but, in my experience, the out-of-box behaviour is sufficient for almost all needs.
I love this implementation with [CallerMemberName] in C# 5 as described here
protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
...
}
Just make the parameter optional and decorate it with the CallerMemberName attribute.
Your property now looks like this:
public DateTime Time
{
get { return this.model.DateTime; }
set
{
this.model.DateTime = value;
NotifyPropertyChanged();
}
}
i usually like to use this implementation to notify the changes in my properties
class IWillNotifyYou : INotifyPropertyChanged
{
private int _firstProperty;
public int FirstProperty
{
get { return _firstProperty; }
set
{
if (value != _firstProperty)
{
_firstProperty = value;
OnPropertyChanged("FirstProperty");
}
}
}
private string _secondProperty;
public string SecondProperty
{
get { return _secondProperty; }
set
{
if (value != _secondProperty)
{
_secondProperty = value;
OnPropertyChanged("SecondProperty");
}
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
note that you could create a specialized event with the name of the property like FirstPropertyChanged
You'll need to identify what role your existing classes play now. WPF is most beneficial using MVVM (Model-View-ViewModel). Identifying the role of your current classes will help determine what needs to be changed (if anything).
Assuming your existing classes are Business Objects, it is up to you to decide whether or not you even need to implement INotifyPropertyChanged. WPF is easiest to work with by binding to ViewModel. The ViewModel is what actually handles the data between the View (WPF screen) and the Model.
The Model can implement INotifyPropertyChanged, but it does not have to. The ViewModel will hold whatever properties are needed for that specific screen (or control) and is probably the path you will want to explore so you do not completely 180* your existing business objects.
Add ": INotifyPropertyChanged" after your class definition:
Public class Test : INotifyPropertyChanged
{
}
Accept ReSharpers recommendation to implement the interface for this class (you might have to install ReSharper for this to work).
Then, in the setter for any property, add this:
OnPropertyChanged("PropertyName")
This means that you can now bind properties in the XAML to this property in this class.
If I have a class like so:
public class MyClass:INotifyPropertyChanged
{
private Visibility isVisible;
private ObservableCollection<string> names;
public Visibility IsVisible
{
get{ return isVisible;}
set { isVisible = value; OnPropertyChanged("IsVisible");}
}
public ObservableCollection<string> Names
{
get { return names;}
set { names = value; OnPropertyChanged("Names");}
}
//ctor
public MyClass(){
names = new ObservableCollection<string>();
}
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Before any one beheads me - I have done quite a bit of looking up and have found a mixed bag of answers...
Do I modify the public or private properties/variables for use in my bindings? i.e. I have an issue where adding to names collection will trigger OnPropertyChanged and changing isVisible will NOT trigger OnPropertyChanged. My assumption is that this is because names is an ObservableCollection where as isVisible is not but I am not sure...
If I am supposed to uses the public properties - what is the need for having the private ones?
You don't need a private property, only a private field would be enough so replace:
private Visibility isVisible {get; set;}
with
private Visibility isVisible;
If I am supposed to uses the public properties - what is the need for
having the private ones?
You cannot use Auto-properties with INotifyPropertyChanged. That is why you need a backing field for your property IsVisible.
See: An elegant way to implement INotifyPropertyChanged
So I think you are confusing Properties and Fields (aka variables).
public class Example()
{
public int FieldExample;
private int _propertyExample;
public int PropertyExample
{
get
{
return _propertyExample;
}
set
{
_propertyExample = value;
}
}
}
In simple usage scenarios, the difference between a field and a property isn't obvious. But properties have different plumbing under the hood that allows them to take advantage of reflection and binding. For WPF, this means you've got to have public properties. Best practice for a Public Property is associate it with a private (or protected) field - and that field name is usually either prefixed with an _ and/or starts with lower case character. This is called a "backing field."
The private backing field holds the actual data, the public property is just the means by which other classes can interact with that data. Inside the get and set blocks, you can place any code you want: instead of returning my backing field, I could instead put: return 5;. It's not useful, and it's poor practice, but I can. Generally, the code that resides in your get and set blocks should still set or get the value; although you might validate the input first, and/or format it first. The pattern you are implementing in your sets for WPF raises an event that the property has changed. Other parts of your program are listening for that event so they know to update the UI.
So in your code, if you only change the backing field and don't raise an event that there has been a change, the UI will not update. You might desire this behavior if you are performing a complex action on an object, and want to hold off performing an UI update until a complete batch of items are finished, but that's an optimization and for starters you are probably better off always accessing/setting to the Public Property.
I have a big problem with MVVM design. I am trying to catch every PropertyChanged of my inner nested objects, including futhermore propertchanged of their nested objects, inside my ViewModel but I dont know how to do it.
Here is my structure:
class MyVM
{
public MyVM()
{
this.SomeData = new SomeData();
this.SomeData.NestedObj = new MyNestedDat();
this.SomeData.Str = "This tiggers propertychanged inside MyDat class";
// this triggers propertychanged event inside MyNestedDat class
this.SomeData.NestedObj.Num = 123;
}
// and here should be a method where i catch all possibe propertychanges from my nested objets and their nested objets, how do i do that?
public MyDat SomeData
{
get;
set;
}
}
class MyDat : INotifyPropertyChanged
{
private string str;
public string Str;
{
get { return this.str;}
set
{
this.str = value;
this.PropertyChanged(this, "Str");
}
}
publicMyNestedDat NestedObj
{
get;
set;
}
}
class MyNestedDat : INotifyPropertyChanged
{
private int num;
public int Num
{
get{ return this.num;}
set
{
this.num = value;
this.PropertyChanged(this, "Num");
}
}
}
How do i get this to work? I am really clueless where to start.
MyNestedDat class throws PropertyChanged, MyDat class throws propertychanged and i want to catch them all inside my viewmodel. How can i do that?
In my opinion there are a few conceptual things wrong with what you are asking. Just imagine you get a solution that works for your scenario (that you are happy with) and consider the following:
What happens if another layer is added? do you still expect it to work the same?
Should property changes be propagated (viewModel1.propA notifies viewModel2.PropA)?
Should property changes be transformed (viewModel1.SomeProp notifies ViewModel2.AnotherProp)?
Is performance a concern? how will this perform if you need to propagate the property changed events through many levels?
This should be raising alarm bells that the current approach is not the right path to tread.
What you need is a way to provide communication between your viewModels in a loosely coupled way so that you viewModels do not even need to know about each others existence. The beauty of this is that this will also work in other situations not just for property changes.
For your case of property changed events, one viewModel wants to know when something happens (it could be something other than a property changed event, remember). This means the other viewModel needs some way of saying "Hey, a property has changed" (or "My state has changed", "That database call has finished" etc).
Now in C# you can provide events which provide this feature....except, now your objects know about each other which leaves you with the same problem you had before.
To overcome this problem you need another object, a mediator (lets call it Messenger in this example), whose sole purpose is to handle the message passing between the objects so that they can live in ignorance of each other.
The general idea is this. In the viewModel that provides notifications you might do something like this:
public string MyProp
{
get { return _myProp; }
set
{
_mProp = value;
OnPropertyChanged("MyProp");
Messenger.PostMessage(new VMChangedMessage { ViewModel = this, PropertyName = "MyProp" });
}
}
And in the viewModel that is interested in the event you might do something like this:
public class ViewModel2
{
public ViewModel2()
{
Messenger.Subscribe<VMChangedMessage>(handleMessage);
}
private void handleMessage(VMChangedMessage msg)
{
// Do something with the information here...
}
}
Notice that the two viewModels never reference each other. They are now loosely-coupled.
There are a number of pre-existing implementations already available and it isn't difficult to create your own (the messenger basically keeps a list of objects that are interested in a certain message and iterates the list when it needs to notify the interested parties). There are a few things that can be implemented differently (some implementations just pass string messages around rather than encapsulating the information in objects, and some handle the clean-up of observers automatically).
I would recommend using Josh Smiths (excellent) MVVM Foundation which includes a messenger class. It's also open source so you can see how it works.
There is no clear constraint about what PropertyName should contains in PropertyChangedEventArgs.
See Subscribe to INotifyPropertyChanged for nested (child) objects.
Here is an example :
class A : BaseObjectImplementingINotifyPropertyChanged {
private string m_name;
public string Name {
get { return m_name; }
set {
if(m_name != value) {
m_name = value;
RaisePropertyChanged("Name");
}
}
}
}
class B : BaseObjectImplementingINotifyPropertyChanged {
private A m_a;
public A A {
get { return m_a; }
set {
if(m_a != value) {
if(m_a != null) m_a.PropertyChanged -= OnAPropertyChanged;
m_a = value;
if(m_a != null) m_a.PropertyChanged += OnAPropertyChanged;
RaisePropertyChanged("A");
}
}
}
private void OnAPropertyChanged(object sender, PropertyChangedEventArgs e) {
RaisePropertyChanged("A." + e.PropertyName);
}
}
B b = new B();
b.PropertyChanged += (s, e) => { Console.WriteLine(e.PropertyName); };
b.A.Name = "Blah"; // Will print "A.Name"
The best thing to do here is to separate the idea of a Model and a ViewModel.
By having a ViewModel object that is flatter than the Model you can avoid this scenario. Using an automatic mapping tool like Automapper then allows you to map the Model to the ViewModel and vice versa.
https://github.com/AutoMapper/AutoMapper/wiki/Flattening
class MyDatViewModel : INotifyPropertyChanged
{
public string Str
{
// ... Get Set
}
public int NestedObjNum
{
// ... Get set
}
}
// Configure AutoMapper
Mapper.CreateMap<MyDat, MyDatViewModel>();
// Perform mapping
MyDatViewModel viewModel = Mapper.Map<MyDat, MyDatViewModel>(someData);