Minimizing WPF boilerplate code on bound properties - c#

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.

Related

C# How to pass Properties in a generic way without Reflections?

Currently I have three properties in class MyClassOne. The number of properties may increase in future. Whenever any of the property changes, I need to call a method called SavePropertyToFile() which is responsible to set the corresponding property (AnotherPropertyOne, AnotherPropertyTwo or AnotherPropertyThree) of another class.
My sample code (which is of course not working) is below:
Class MyClassOne
{
public bool PropertyOne
{
get => _propertyOne;
set
{
if (_propertyOne == value)
return;
_propertyOne = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyOne); //<------------------
NotifyOfPropertyChange(() => _propertyOne);
}
}
public bool PropertyTwo
{
get => _propertyTwo;
set
{
if (_propertyTwo == value)
return;
_propertyTwo = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyTwo); //<------------------
NotifyOfPropertyChange(() => _propertyTwo);
}
}
public bool PropertyThree
{
get => _propertyThree;
set
{
if (_propertyThree == value)
return;
_propertyThree = value;
SavePropertyToFile(value, MyAnotherClass.AnotherPropertyThree); //<------------------
NotifyOfPropertyChange(() => _propertyThree);
}
}
SavePropertyToFile(bool value, TProperty myPropertyOfAnotherClass)
{
myPropertyOfAnotherClass = value;
// Some more lines of Code
RefreshProgram();
}
}
In the code above, I need to find a way so that I can parse the correct property of AnotherClass. I already had a look at this another SO question, but I am not able to apply any of the answer for my case.
How can I parse different properties to a method?
Update: As per the suggestions in the comments, I have tried to use "Nathan Baulch" answer's option-2 but then I getting warning/error wiggly line as shown in the screenshot below:
If the idea is to make several classes use the same property, my approach is to wrap the values in a class:
public class Changeable<T> : INotifyPropertyChanged
{
private T currentValue;
public Changeable(T initialValue) => currentValue = initialValue;
public T Value
{
get => currentValue;
set
{
if (!EqualityComparer<T>.Default.Equals(currentValue, value))
{
this.currentValue = value;
Changed?.Invoke(this, value);
OnPropertyChanged(nameof(Value));
}
}
}
public event EventHandler<T> Changed;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
You can then just let both classes use the same actual object. This approach can also be used to cut down on boilerplate for wpf, instead of declaring getters and setters you can just write:
public Changeable<bool> MyProperty {get;} => new (false);
Just remember to update the binding: {Binding MyProperty.Value}
You can also write various extensions. For example if you want to update one property whenever some other property updates:
MyProperty.BindTo(MyIntProperty, intValue => intValue > 0);
So whenever MyIntProperty changes sign, the MyProperty would be updated and raise its changed event. I'm not going to include all the code behind this, since it is fairly large, but I hope you get the idea. This approach makes it possible to link various UI values in a convenient way, but it can also make the code more difficult to understand and debug, so some caution is advised.

Don't Understand How INotifyPropertyChanged function

INotifyPropertyChanged interface implementation (appropriate event) and helper method works fine in my code but I really don't understand how it works. My book does a poor job of explaining it to me in my opinion or I am not very smart.
We have a separate class Car.cs added to my solution with the following code (example from the book) and it should work in TwoWay Binding regards to TextBox control in my WPF application being changed when object's instance is being changed too:
public class Car: INotifyPropertyChanged
{
private string _make;
private string _model;
public event PropertyChangedEventHandler PropertyChanged;
public Car()
{
}
public string Make
{
get { return _make; }
set
{
if (_make != value)
{
_make = value;
OnPropertyChanged("Make");
}
}
}
public string Model
{
get { return _model; }
set
{
if(_model != value)
{
_model = value;
OnPropertyChanged("Model");
}
}
}
private void OnPropertyChanged (string propertyName)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}}
And here is the code which I made myself after learning Microsoft learning materials and it looks and works better imo:
public class Car: INotifyPropertyChanged
{
private string _make;
private string _model;
public event PropertyChangedEventHandler PropertyChanged;
public Car()
{
}
public string Make
{
get { return _make; }
set
{
_make = value;
OnPropertyChanged();
}
}
public string Model
{
get { return _model; }
set
{
_model = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged ()
{
if (PropertyChanged != null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
}
} }
So I have the foolowing question regards to example from the book (the first one):
What this part means? What's the point to check if property not equals to value (never saw anything like it before when during the Set part of the property you check that field of the class checked for being not equalled to value)? Code works even without it. No explanation for it in the book. Here is the code under question:
set
{
if (_make != value)
{
_make = value;
OnPropertyChanged("Make");
}
Why if you'll write the name of the property without brackets in the above-mentioned small piece of code (in subparagraph one) then it will not work? I meant if you'll write Make then it does not work and you should write "Make".
Thanks in advance!
Here is a basic example of how to correctly implement the IPropertyChanged interface, which notifies the view to re-draw on property changing its value.
public class PropertyChangedEventStackOverflow : INotifyPropertyChanged
{
//We have a private property of the same public property. This allows us to store the privous value of the property which we can
//get at any time when we ask.
string name = string.Empty;
//Public property is required to do two-way-binding or one-way-binding from model to the view.
public string Name
{
get => name;
set
{
if (name == value) //Check if the private property is already equals to the exact same value to avoid extra memory
return; //and uneccasary screen re-drawing. So we just return and avoid calling the OnPropertyChanged.
name = value; //If the value is different meaning we want to notify view and tell it to re-draw the screen for us.
OnPropertyChanged(nameof(Name));
}
}
//We create this helper metod to avoid doing something like this every single time we want to notify
string surname = string.Empty;
public string Surname
{
get => surname;
set
{
if (surname == value)
return;
surname = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Surname)));
}
}
//Here is the method to create for OnPropertyChangedEvent
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If you want to go step further and want to have an Action when your property has changed you can do something like this. backingStore type T = private property
public bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
You can give it an action to execute if once a property has changed.
Because raising the PropertyChanged event can be expensive in terms of performance (for example when a complex view has to redraw itself in order to display the latest data), it's a recommended pattern to raise it only when necessary.
It's only necessary when the new value has changed. To update the binding when the new value and the old value are the same is redundant and potentially expensive.
The following example is a very verbose version to highlight what's going on:
private string myProperty;
public string MyProperty
{
get => this.myProperty;
set
{
string newValue = value;
string oldValue = this.MyProperty;
if (newValue != oldValue)
{
this.myProperty = newValue;
OnPropertyChanged(nameof(this.MyProperty));
}
}
}
For reasons of performance, passing an empty string (string.Empty or "") or null to the PropertyChangedEventArgs instance, instead of the property name, will instruct the binding engine to update all bindings of the class that raises the PropertyChanged event.
In other words, it's like raising the PropertyChanged event for all properties at once.
That's why you only do this when this behavior is explicitly desired (for example when you reset and clear all properties):
/* Will raise the PropertyChanged event for ALL properties defined by the declaring type */
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
/* Will raise the PropertyChanged event ONLY for the particular property that matches the provided property name */
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MyProperty"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.MyProperty)));
"Make" is called a string literal. Every literal enclosed in double-quotes is interpreted as plain text by the compiler (string literal). If it wasn't the double-quotes, the compiler expects the value to be a language object (for example a variable or type).
If you check the signature of the constructor of PropertyChangedEventArgs you will see that it expects the property name as string.
Using Make (without the double-quotes) is a simple value reference (local or member variable) that returns the value of the Make property and not the name of the property (member name).
The property name is required to tell the binding engine which property has changed and therefore which Binding needs to be updated.
The following example assumes that the property Make is set to a value of "BMW":
public string Make
{
get => this.make;
set
{
// For example 'value' is "BMW"
this.make = value;
// Invocation a)
// Pass the property name as argument.
OnPropertyChanged("Make"); // Same as: OnPropertyChanged(nameof(this.Make))
// Invocation b)
// Pass the property value as argument
OnPropertyChanged(Make);
// Verbose version of b)
string modelMake = Make;
OnPropertyChanged(modelMake);
}
}
Version b) won't work, because the binding engine needs the property's member name and not the property value.
See Microsoft Docs: Strings and string literals to learn about strings.
See INotifyPropertyChanged to learn the recommended pattern to implement the interface
Remarks
Checking variable (for example an event delegate) for null using an if statement and the Null-conditional operator ?. or ?[] is redundant.
In high performance code you would want to avoid the redundant double checking.
The following versions are the same:
private void OnPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private void OnPropertyChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
What's the point to check if property not equals to value
it is just to avoid unnecessary updates. It is not strictly required. Whenever you have events to signal that something is updated it is often a good idea to check if the thing actually changed, since you do not know what is listening to the event, it could potentially trigger some slow computations.
Why if you'll write the name of the property without brackets in the above-mentioned small piece of code (in subparagraph one) then it will not work?
Because the event needs a string. You can let the compiler insert this string for you by writing OnPropertyChanged(nameof(Make)); You can also use CallerMemberName attribute to let the compiler insert this automatically, just call OnPropertyChanged() within your property.
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Going one step further you you can create a method that does both the comparison, field update, and event, letting you write a one line setter => Set(ref _make, value);
private void Set<T>(ref T field, T value, [CallerMemberName] string caller = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(caller);
}
}
However, my preference tend to be to wrap all of this inside a separate class:
public class Changeable<T> : INotifyPropertyChanged
{
private T currentValue;
public Changeable(T initialValue) => currentValue = initialValue;
public T Value
{
get => currentValue;
set
{
if (!EqualityComparer<T>.Default.Equals(currentValue, value))
{
this.currentValue = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
That way you can declare a property like public Changeable<string> Make {get;} = new (""); and bind to it like {Binding Make.Value}
INotifyPropertyChanged is an interface which only requires a class to implement it's requirements. Which is basically "just" an Event.
public interface INotifyPropertyChanged
{
//
// Summary:
// Occurs when a property value changes.
event PropertyChangedEventHandler? PropertyChanged;
}
The Method OnPropertyChanged is just a "helper" to raise this event. But the important part here, is that the INotifyPropertyChanged implementation by itself is not doing anything.
However WinForms and WPF are automatically subscribing in some higher-hirachy classes to such Event. They identify such classes through reflection and add themself to one of the subscribers to the PropertyChanged Event - which your class still needs to raise.
For this purpose you need to override the Setters of your properties:
public string Examle
{
get { return _field; }
set
{
_field = value;
OnPropertyChanged(); // <---
}
}
Otherwise the subscriber will never be notified. However the whole purpose of this is to decouple the handling of changes from the "Model" so your class which act's as container to the data - since the container can be passed to different forms, which might want to handle this container different.
The check if the value actually changed is only something you CAN do - to prevent the notification of the subscriber (let's say your data grid) to re-run all validations you've setup for such property. To avoid unnecessary runs of such validations this if will prevent notifying subscribers of such changes where a value get's re-assigned, but it remains the same.
The auto-wiring of the subscriber however is abstraced for you as developer, which can create difficulties for beginners to understand - however makes your life much more easier on the long run, as you can rely on the Framework as such (less for you to implement, test, etc.) so it's just a "convention" to use INotifyPropertyChange Interface, but the basic building blocks are general C# constructs.
Hope this helped a little :)

How do I correctly implement INotifyPropertyChanged for my bool property and bind to CheckBox.IsChecked?

Novice here. I've been trying to wrap my head around databinding, and wanted to do try out two-way binding of a checkbox in the view to a boolean in a separate class that I've called "State". The point is to ensure that they are always in sync.
So I've made a checkbox in the view and bound it to the aforementioned boolean property in the State-class, accompanied by a button that bypasses the checkbox and toggles the boolean property directly (aptly labeled 'Ninja!'). The point was to test that the checkbox' databinding reacts when the property changes. However, I can't for the best of me figure out how the OnPropertyChanged-method is supposed to be invoked when the property changes.
Here's what I have so far:
<CheckBox x:Name="checkBox" Content="CheckBox" HorizontalAlignment="Left" Margin="232,109,0,0" VerticalAlignment="Top" IsChecked="{Binding Checked, Mode=TwoWay}"/>
<Button x:Name="button" Content="Ninja!" HorizontalAlignment="Left" Margin="228,182,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
And the code for the "State"-class I've made:
namespace TestTwoWayBinding
{
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged(Checked);
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Checked));
}
}
}
}
And the code-behind on the view for initialization and handling the events:
namespace TestTwoWayBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private State _state;
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
}
private void button_Click(object sender, RoutedEventArgs e)
{
_state.Toggle();
}
}
}
From what I gather, OnPropertyChanged expects a String propertyName, but I don't know what that would entail here. When I put in the name of the property (Checked), then that naturally refers to a boolean, not a string. What am I not getting? And what else am I doing wrong, as the checkbox doesn't register the property change when I change it through the button?
The two answers which suggest you pass the string literal "Checked" will work, but IMHO aren't the best way to do it. Instead, I prefer using [CallerMemberName] when implementing the OnPropertyChanged() method. (I have no idea what that third answer is all about…it doesn't appear to have anything to do with this question, and I'd guess it was just copy/pasted from somewhere else).
Here's an example of how I'd write your State class:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get { return _checked; }
set { _checked = value; OnPropertyChanged(); }
}
public void Toggle()
{
Checked = !Checked;
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The key here is that the parameter marked with [CallerMemberName] will automatically be filled in with the correct value from the caller, simply by not passing any value. The default value of null is there just so the compiler will allow the caller to not pass a value.
Note that I also simplified the Toggle() method. There's no need to use an if statement to transform one bool value into another; that's what the Boolean operators are there for.
I also changed the OnPropertyChanged() method so that it's thread-safe, i.e. won't crash if some code unsubscribes the last handler from the PropertyChanged event between the time the event field is compared to null and the time the event is actually raised. Typically, this is a non-issue as these properties are nearly always accessed only from a single thread, but it's easy enough to protect against and is a good habit to get into.
Note that in C# 6, you have the option of just writing PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); for the method body. Not everyone is using the new compiler 100% of the time yet, so I just mention that as an optional choice for you.
Naturally, you also need to set the DataContext correctly, as shown in one of the other answers:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
Though, personally, I'm not sure I'd bother with the constructor. You appear to have no other code that would set checkBox.IsChecked, so it seems to me that you're always going to get the default value anyway. Besides, you can't create your view model class in XAML if it doesn't have a parameterized constructor; in the future, you may prefer to configure your DataContext like that. E.g.:
<Window.DataContext>
<l:State Checked="True"/>
</Window.DataContext>
And in the window's constructor:
public MainWindow()
{
InitializeComponent();
_state = (State)this.DataContext;
}
See also the related Q&A Automatically INotifyPropertyChanged. The question there is really about something different — they want to implement the interface without having to explicitly write anything in the property setter — but for better or worse, the answers they got are really more about your scenario, where it's just a question of simplifying the property setter implementation rather than making it completely automatic.
I have to admit, I would've thought there would have been another question already with which to mark yours as a duplicate. And I did find lots of related questions. But nothing that focuses directly on just "how do I implement and use a view model that implements INotifyPropertyChanged?", which is really what your question seems to be about.
Addendum:
I did some more searching, and while none of these seem like they would be considered exact duplicates per se, they all have good information that help address the question about implementing INotifyPropertyChanged:
Use of Attributes… INotifyPropertyChanged
INotifyPropertyChanged for model and viewmodel
BindableBase vs INotifyChanged
How to write “ViewModelBase” in MVVM (WPF)
You are real close. You need to make 2 small changes and your test works:
Assign the DataContext of your Window to the _state variable.
Put the string "Checked" into the OnPropertyChanged and pass propertyName to the PropertyChangedEventArgs in the OnPropertyChanged method.
So your MainWindow ctor becomes:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
and the State class file looks like:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
As a novice, I recommend you learn more about Model-View-ViewModel MVVM Design Pattern. It is a common pattern with WPF and helps encourage separation of concerns (keeping your business logic out of your user interface logic)
The OnPropertyChanged method expects the Checked property's name as argument - at the moment, you're passing its value!
This means, change the Checked property declaration to:
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}

Property Getters and Setters when implementing INotifyPropertyChanged?

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.

Trigger an event when something changes in a class

Is it possible to trigger some event when something changes in a given class?
E.g. I have a class that has 100 fields and one of them is being modified externally or internally. Now I want to catch this event. How to do this?
The most I wonder if there is a trick to do this quickly for really extended classes.
As a best practice, convert your public fields to manual properties and implement your class with the INotifyPropertyChanged interface in order to raise a change event.
EDIT: Because you mentioned 100 fields I would suggest you to refactor your code like in this great answer: Tools for refactoring C# public fields into properties
Here is an example of it:
private string _customerNameValue = String.Empty;
public string CustomerName
{
get
{
return this._customerNameValue;
}
set
{
if (value != this._customerNameValue)
{
this._customerNameValue = value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Check this out: INotifyPropertyChanged Interface

Categories

Resources