The answer to this question has been edited to say that in C# 6.0, INotifyPropertyChanged can be implemented with the following OnPropertyChanged procedure:
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
However, it isn't clear from that answer what the corresponding property definition should be. What does a complete implementation of INotifyPropertyChanged look like in C# 6.0 when this construction is used?
After incorporating the various changes, the code will look like this. I've highlighted with comments the parts that changed and how each one helps
public class Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
//C# 6 null-safe operator. No need to check for event listeners
//If there are no listeners, this will be a noop
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
// C# 5 - CallMemberName means we don't need to pass the property's name
protected bool SetField<T>(ref T field, T value,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private string name;
public string Name
{
get { return name; }
//C# 5 no need to pass the property name anymore
set { SetField(ref name, value); }
}
}
I use the same logic in my project. I have a base class for all view models in my app:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Every view model inherits from this class. Now, in the setter of each property I just need to call OnPropertyChanged().
public class EveryViewModel : PropertyChangedBase
{
private bool initialized;
public bool Initialized
{
get
{
return initialized;
}
set
{
if (initialized != value)
{
initialized = value;
OnPropertyChanged();
}
}
}
Why does it work?
[CallerMemberName] is automatically populated by the compiler with the name of the member who calls this function. When we call OnPropertyChanged from Initialized, the compiler puts nameof(Initialized) as the parameter to OnPropertyChanged
Another important detail to keep in mind
The framework requires that PropertyChanged and all properties that you're binding to are public.
I know this question is old, but here is my implementation
Bindable uses a dictionary as a property store. It's easy enough to add the necessary overloads for a subclass to manage its own backing field using ref parameters.
No magic string
No reflection
Can be improved to suppress the default dictionary lookup
The code:
public class Bindable : INotifyPropertyChanged
{
private Dictionary<string, object> _properties = new Dictionary<string, object>();
/// <summary>
/// Gets the value of a property
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
protected T Get<T>([CallerMemberName] string name = null)
{
object value = null;
if (_properties.TryGetValue(name, out value))
return value == null ? default(T) : (T)value;
return default(T);
}
/// <summary>
/// Sets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="name"></param>
protected void Set<T>(T value, [CallerMemberName] string name = null)
{
if (Equals(value, Get<T>(name)))
return;
_properties[name] = value;
OnPropertyChanged(name);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
used like this
public class Item : Bindable
{
public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } }
}
Related
Situation:
UWP app using MVVM and Xaml for UI
View Models are derived from a ModelBase class implementing the INotifyPropertyChanged interface
Problem:
when executing a specific UI test using the affected class, in some cases the application throws an InvalidCastException during the PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)) call. The propertyName is set correctly using the [CallerMemberName] which was verified using a debug log entry. The SetProperty method is called in the DependencyProperty with a backing field. A DebugConverter used in the Xaml element which is bound to the PropertyChanged event shows a valid conversion but the setting of the bound element fails. It looks like a Double to Double cast is not possible which makes no sense.
Question
Does anyone have an idea what the reason for this exception could be?
Code
Control.xaml
Maximum="{Binding TimeControlCanvasWidth, Converter={StaticResource DebugConverter}}" />
ModelBase.cs
public class ModelBase : INotifyPropertyChanged
{
/// <summary>
/// The event raised, when the property changed
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
try
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
catch (System.InvalidCastException)
{
LogManager.Current.GetLogger(GetType()).LogCritical(() => $"#####{nameof(OnPropertyChanged)}" +
$" - InvalidCastException | propertyName={propertyName}");
}
}
protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
{
if (field?.Equals(value) == true)
{
return false;
}
field = value;
// ReSharper disable once ExplicitCallerInfoArgument
OnPropertyChanged(propertyName);
return true;
}
}
}
ViewModel.cs
public class TimeControlViewModel : ModelBase
{
private double _timeControlCanvasWidth;
public double TimeControlCanvasWidth
{
get => _timeControlCanvasWidth;
set
{
if (SetProperty(ref _timeControlCanvasWidth, value))
{
// do sth.
}
}
}
}
I have a service with reference code below:
[System.Xml.Serialization.SoapTypeAttribute(Namespace="urn:customer")]
public partial class Receipt : object, System.ComponentModel.INotifyPropertyChanged {
private int counternoField;
private double activekwhField;
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute("counter-no")]
public int counterno {
get {
return this.counternoField;
}
set {
this.counternoField = value;
this.RaisePropertyChanged("counterno");
}
}
/// <remarks/>
[System.Xml.Serialization.SoapElementAttribute("active-km")]
public double activekm {
get {
return this.activekm Field;
}
set {
this.activekmField = value;
this.RaisePropertyChanged("activekm");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
[System.Xml.Serialization.SoapTypeAttribute(Namespace="urn:customer")]
public partial class ArrayOfReceipt : object, System.ComponentModel.INotifyPropertyChanged {
private Receipt[] itemField;
/// <remarks/>
public Receipt[] item {
get {
return this.itemField;
}
set {
this.itemField = value;
this.RaisePropertyChanged("item");
}
}
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if ((propertyChanged != null)) {
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
}
}
}
And, when I want to create an instance of "ArrayOfReceipt" or set a value, or access it, I always encounter the same problem: System.NullReferenceException.
This is the code when I try to create:
var prev_Cons = new myService.Receipt();
prev_Cons.counterno = 1;
prev_Cons.activekm = 3265;
myService.ArrayOfReceipt prev_ConsArr = new myService.ArrayOfReceipt();
prev_ConsArr.item.SetValue(prev_Cons, 0);
Unfortunatelly, prev_ConsArr.item is always null, and I cannot initialize it. Please show me a way to initialize and set some value to this object. Thanks in advance.
The way your code is written, you can initialize prev_ConsArr.item like this:
prev_ConsArr.item = new Receipt[3];
That would create a new ArrayOfReceipt that could hold three Receipt objects. You could also create a constructor for your ArrayOfReceipt class that initializes item. Either of these methods will eliminate your NullReferenceException
Looking at the way you are using your ArrayOfReceipt class, you may want to consider changing the type of item to List<Receipt>. That would make it easier to change the number of Receipt classes you are storing.
Depending on what you are trying to do, you may also want to create an AddReceipt method in ArrayOfReceipts and move yourPropertyChanged event to that method. Right now, the PropertyChanged event will only fire when your ArrayOfReceipts class overwrites its array of Receipts.
I'm trying to fire PropertyChanged via reflection and I'm having some issues.
The following code works:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string m_test = string.Empty;
public string Test
{
get
{
return m_test;
}
set
{
m_test = value;
Notify();
}
}
protected void Notify([CallerMemberName] string name = null)
{
var handler = PropertyChanged;
if (handler != null && name != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class ViewModel : ViewModelBase
{
private string m_test2 = string.Empty;
public string Test2
{
get
{
return m_test2;
}
set
{
m_test2 = value;
Notify();
}
}
}
However, I have added an extension method to INotifyPropertyChanged that would raise it via reflection.
Instead of Notify() I could call this.Notify() instead, which is defined like so:
/// <summary>
/// Invoke sender's PropertyChanged event via Reflection
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="prop">The Property name that has changed</param>
public static void NotifyPropertyChanged(this INotifyPropertyChanged sender, [CallerMemberName] string prop = null)
{
var senderType = sender.GetType();
var methodInfo = senderType.GetField("PropertyChanged", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (methodInfo != null)
{
var delegates = (MulticastDelegate)methodInfo.GetValue(sender);
if (delegates != null)
{
foreach (var handler in delegates.GetInvocationList())
{
handler.Method.Invoke(handler.Target, new object[] { sender, new PropertyChangedEventArgs(prop) });
}
}
}
}
Unfortunately, GetField returns null for ViewModel in the example above.
Is there a way to reflect the parent's event?
I'm thinking of iterating over the base classes, but I'm hoping for a better/easier way.
I think you are going about this the wrong way.
You are breaking the framework's encapsulation of events only being raised by the declaring (owning) instance. By using a publicly available extension method that anyone can call, you are opening a can of worms.
A better solution would be to use a protected method in a base class, as was done in your "the following code works" example.
But if you are really determined on doing it, it can obviously be done.
The extension method below can be used if you want to break the normal protections and encapsulation around events.
public static class ProperyChangedEventExtensions
{
public static void RaisePropertyChanged<T, P>(this T sender, Expression<Func<T, P>> propertyExpression) where T : INotifyPropertyChanged
{
Raise(typeof(T), sender, (propertyExpression.Body as MemberExpression).Member.Name);
}
public static void RaisePropertyChanged(this INotifyPropertyChanged sender, [CallerMemberName] string prop = null)
{
Raise(sender.GetType(), sender, prop);
}
private static void Raise(Type targetType, INotifyPropertyChanged sender, string propName)
{
var evtPropType = targetType.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
var evtPropVal = (PropertyChangedEventHandler)evtPropType.GetValue(sender);
evtPropVal(sender, new PropertyChangedEventArgs(propName));
}
}
Usage example (including hopefully some cases that will make you reconsider this approach):
class MyViewModel : INotifyPropertyChanged
{
// The compiler will complain about this:
// Warning 3 The event 'MyNamespace.MyViewModel.PropertyChanged' is never used
public event PropertyChangedEventHandler PropertyChanged;
private string _myProp;
public string MyProp
{
get { return _myProp; }
set
{
_myProp = value;
this.RaisePropertyChanged();
}
}
public readonly int MyImmutableValue;
}
// ...
var vm = new MyViewModel();
vm.PropertyChanged += (sender, evt) => Console.WriteLine("Prop changed {0}", evt.PropertyName);
vm.MyProp = "abc";
vm.RaisePropertyChanged(x => x.MyProp);
vm.RaisePropertyChanged("MyProp");
vm.RaisePropertyChanged("Un Oh. Do we have a problem");
vm.RaisePropertyChanged(x => x.MyImmutableValue);
vm.RaisePropertyChanged("MyImmutableValue");
/// <summary>
/// Invoke sender's PropertyChanged event via Reflection???
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="prop">The Property name that has changed</param>
public static void NotifyPropertyChanged(this INotifyPropertyChanged sender, PropertyChangedEventHandler handler, [CallerMemberName] string prop = null)
{
handler(sender, new PropertyChangedEventArgs(prop));
}
use it like this?
class MyViewModel : INotifyPropertyChanged
{
// The compiler will complain about this:
// Warning 3 The event 'MyNamespace.MyViewModel.PropertyChanged' is never used
public event PropertyChangedEventHandler PropertyChanged;
private string _myProp;
public string MyProp
{
get { return _myProp; }
set
{
_myProp = value;
this.Notify(this.PropertyChanged);
}
}
}
I need to add logic of a property setter.
For example, I have a property named "CurrentTab":
private WorkspaceViewModel _currentTab;
public WorkspaceViewModel CurrentTab
{
get
{
return _currentTab;
}
set
{
_currentTab = value;
OnPropertyChanged("CurrentTab");
}
}
This is all good and works, but I want to be able to just define it like this:
public WorkspaceViewModel CurrentTab { get; set; }
So that the system automatically performs the OnPropertyChanged() function for the property name after the setter has run without me adding any specific code.
How to identify which properties need to follow this logic is no problem, I just need to find a way how to actually do it.
I want to make this simpler because I'll be having quite a lot of those kind of properties and I'd like to keep it clean.
Is there a way?
Any help is much appreciated!
Take a look: Fody. There is an add-in for INotifyPropertyChange: github
It is manipulating IL code while building the solution.
You need only to add attribute to view model:
[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);
}
}
}
When code gets compiled:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get { return givenNames; }
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get { return familyName; }
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName
{
get
{
return string.Format("{0} {1}", GivenNames, FamilyName);
}
}
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
This can be achieved using PostSharp, which is an Aspect Oriented Programming approach:
In computing, aspect-oriented programming (AOP) is a programming
paradigm that aims to increase modularity by allowing the separation
of cross-cutting concerns. AOP forms a basis for aspect-oriented
software development.
You can implement this using an Aspect called InstanceLevelAspect:
/// <summary>
/// Aspect that, when apply on a class, fully implements the interface
/// <see cref="INotifyPropertyChanged"/> into that class, and overrides all properties to
/// that they raise the event <see cref="INotifyPropertyChanged.PropertyChanged"/>.
/// </summary>
[Serializable]
[IntroduceInterface(typeof(INotifyPropertyChanged),
OverrideAction = InterfaceOverrideAction.Ignore)]
[MulticastAttributeUsage(MulticastTargets.Class | MulticastTargets.Property,
Inheritance = MulticastInheritance.Strict)]
public sealed class NotifyPropertyChangedAttribute : InstanceLevelAspect,
INotifyPropertyChanged
{
/// <summary>
/// Field bound at runtime to a delegate of the method OnPropertyChanged
/// </summary>
[ImportMember("OnPropertyChanged", IsRequired = false)]
public Action<string> OnPropertyChangedMethod;
/// <summary>
/// Method introduced in the target type (unless it is already present);
/// raises the <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
[IntroduceMember(Visibility = Visibility.Family, IsVirtual = true,
OverrideAction = MemberOverrideAction.Ignore)]
public void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this.Instance,
new PropertyChangedEventArgs(propertyName));
}
}
/// <summary>
/// Event introduced in the target type (unless it is already present);
/// raised whenever a property has changed.
/// </summary>
[IntroduceMember(OverrideAction = MemberOverrideAction.Ignore)]
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Method intercepting any call to a property setter.
/// </summary>
/// <param name="args">Aspect arguments.</param>
[OnLocationSetValueAdvice,
MulticastPointcut( Targets = MulticastTargets.Property,
Attributes = MulticastAttributes.Instance)]
public void OnPropertySet(LocationInterceptionArgs args)
{
// Don't go further if the new value is equal to the old one.
// (Possibly use object.Equals here).
if (args.Value == args.GetCurrentValue())
{
return;
}
// Actually sets the value.
args.ProceedSetValue();
// Invoke method OnPropertyChanged (our, the base one, or the overridden one).
this.OnPropertyChangedMethod.Invoke(args.Location.Name);
}
}
Then, use it on your property like this:
[NotifyPropertyChanged]
public WorkspaceViewModel CurrentTab { get; set; }
This attirubte can also be applied at the class level, if you want all your properties to implement NotifyPropertyChanged. More on the example can be found here
I'm binding an UltraTree control (version 10.3) to a custom data source, like so:
public void Populate(List<FilterDimension> data)
{
DataBindings.Clear();
DataSource = data;
Nodes[0].DataColumnSetResolved.NodeTextColumn = Nodes[0].DataColumnSetResolved.Columns["DisplayText"];
}
My expectation is that changing the DisplayText property on any of the bound FilterDimension objects will cause the UltraTree node's text to update. In reality, the text in the tree does not update, and the PropertyChanged event remains null indicating that the UltraTree doesn't even listen for this notification. How do I get the UltraTree to listen for property changes in FilterDimension?
Here's the relevant code from FilterDimension:
internal class FilterDimension : INotifyPropertyChanged
{
private string _displayText = null;
private string _name = null;
private BindingList<string> _values = new BindingList<string>();
/// <summary>
/// Gets or sets the display friendly name.
/// </summary>
public string Name
{
get { return _name; }
set
{
_name = value;
FirePropertyChangedNotification("Name");
if (_displayText == null) { FirePropertyChangedNotification("DisplayText"); }
}
}
/// <summary>
/// Gets or sets the display text that is used in TreeView nodes. When null, uses the Name.
/// </summary>
public string DisplayText
{
get { return _displayText ?? Name; }
set { _displayText = value; FirePropertyChangedNotification("DisplayText"); }
}
/// <summary>
/// Gets a read/write list of values. Is never null.
/// </summary>
public BindingList<string> Values
{
get { return _values; }
set { _values = value ?? new BindingList<string>(); }
}
#region Events
public event PropertyChangedEventHandler PropertyChanged;
protected void FirePropertyChangedNotification(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
It turns out that all I needed to do was change to BindingList<FilterDimension> instead of List<FilterDimension... I completely missed that the control expects notifications to bubble up from the list.