WPF iDataErrorInfo (validating textboxes) not working as intended - c#

UPDATE: I fixed this by improving my switch statement. Made use of nameof!
I'm trying to validate a set of user inputs from textboxes.
I have my class with interface setup. Snippet of it below:
public class PatientValidation : INotifyPropertyChanged, IDataErrorInfo
{
private string _id;
private string _fname;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string p)
{
PropertyChangedEventHandler ph = PropertyChanged;
if (ph != null)
{
ph(this, new PropertyChangedEventArgs(p));
}
}
public string Id
{
get
{
return _id;
}
set
{
_id = value;
}
}
public string Fname
{
get
{
return _fname;
}
set
{
_fname = value;
}
}
And a switch statement to return error message based on user input:
public string this[string PropertyName]
{
get
{
string result = null;
switch (PropertyName)
{
case "Id":
if (string.IsNullOrEmpty(Id))
result = "ID number is required.";
break;
case "fname":
if (string.IsNullOrEmpty(Fname))
result = "First name is required.";
break;
}
return result;
}
}
My relevant code in XAML:
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<TextBox x:Name="textBox_IDNumber" Text="{Binding Id, Mode=TwoWay, ValidatesOnDataErrors=True}"/>
<TextBox x:Name="textBox_FirstName" Text="{Binding Fname, Mode=TwoWay, ValidatesOnDataErrors=True"}/>
Here's the problem I've run into: ONLY the first textbox (ID) is validated correctly and the error tooltip is shown. None of the other textboxes.
Different bindings and triggers hasn't solved the issue. Any help would be much appreciated.

Typo error (you used lowercase in your switch):
case "fname":
but in your Binding:
Text="{Binding Fname

It doesn't seem that you are calling OnPropertyChanged(nameof(FName)); or OnPropertyChanged(nameof(ID)); within the setters of your properties - thus there will be no notification that the binding has updated, and the IDataErrorInfo.<propertyName> will not be called.

You can using:
public class DataErrorInfoWrapper : DynamicObject, IDataErrorInfo, INotifyPropertyChanged
{
private static readonly ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>> Properties = new ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>>();
private readonly Dictionary<string, PropertyInfo> _typeProperties;
private readonly Func<string, string> _error;
private readonly object _target;
public string this[string columnName] => _error(columnName);
public string Error { get; }
public event PropertyChangedEventHandler PropertyChanged;
public DataErrorInfoWrapper(object target, Func<string, string> error)
{
_error = error;
_target = target;
_typeProperties = Properties.GetOrAdd(_target.GetType(), t => t.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(i => i.SetMethod != null && i.GetMethod != null).ToDictionary(i => i.Name));
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = null;
if (!_typeProperties.TryGetValue(binder.Name, out var property))
return false;
var getter = property.CreateGetter();
result = getter.DynamicInvoke(_target);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (!_typeProperties.TryGetValue(binder.Name, out var property))
return false;
var setter = property.CreateSetter();
setter.DynamicInvoke(_target, value);
RaisePropertyChanged(binder.Name);
return true;
}
protected virtual bool SetProperty<TValue>(ref TValue storage, TValue value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<TValue>.Default.Equals(storage, value))
return false;
storage = value;
RaisePropertyChanged(propertyName);
return true;
}
protected virtual bool SetProperty<TValue>(ref TValue storage, TValue value, Action onChanged, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<TValue>.Default.Equals(storage, value))
return false;
storage = value;
onChanged?.Invoke();
RaisePropertyChanged(propertyName);
return true;
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(propertyName);
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChanged?.Invoke(this, args);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
}
And extensions methods:
public static class TypeExtensions
{
private static readonly ConcurrentDictionary<PropertyInfo, Delegate> Getters = new ConcurrentDictionary<PropertyInfo, Delegate>();
private static readonly ConcurrentDictionary<PropertyInfo, Delegate> Setters = new ConcurrentDictionary<PropertyInfo, Delegate>();
public static Delegate CreateGetter(this PropertyInfo property)
{
return Getters.GetOrAdd(property, p =>
{
var parameter = Expression.Parameter(p.DeclaringType, "o");
var delegateType = typeof(Func<,>).MakeGenericType(p.DeclaringType, p.PropertyType);
var lambda = Expression.Lambda(delegateType, Expression.Property(parameter, p.Name), parameter);
return lambda.Compile();
});
}
public static Delegate CreateSetter(this PropertyInfo property)
{
return Setters.GetOrAdd(property, p =>
{
var parameter = Expression.Parameter(p.DeclaringType, "o");
var valueParm = Expression.Parameter(p.PropertyType, "value");
var delegateType = typeof(Action<,>).MakeGenericType(p.DeclaringType, p.PropertyType);
var lambda = Expression.Lambda(delegateType, Expression.Assign(Expression.Property(parameter, p.Name), valueParm), parameter, valueParm);
return lambda.Compile();
});
}
}

Related

Using INotifyDataErrorInfo with attributes

I am working setting up INotifyDataErrorInfo on a view model, to handle validation with attributes.
I have it working fine in the UI, the text box gets a nice red boarder and the mouse over event says what is wrong.
But I can not work out how in the ViewModel to work out the view model is valid. I am guessing I have to set up the HasErrors. In the examples I have seen they have a variable
private Dictionary<string, List<string>> _PropertyErrors = new Dictionary<string, List<string>>();
But then do nothing to set it.
I would like to check in the Save() method if the view model is valid.
public class CustomerViewModel : EntityBase, INotifyPropertyChanged, INotifyDataErrorInfo
{
public CustomerViewModel ()
{
//SET UP
}
private string _HomePhone;
[Required]
public string HomePhone
{
get { return _HomePhone; }
set
{
if (_HomePhone != value)
{
_HomePhone = value;
PropertyChanged(this, new PropertyChangedEventArgs("HomePhone"));
}
}
}
private void Save()
{
//Break point here
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public bool HasErrors
{
get { return true; }
}
public IEnumerable GetErrors(string propertyName)
{
return null;
}
You can check the HasErrors property.
This is an example implementation of INotifyDataErrorInfo with ValidationAttribute support and providing an example TrySave(), which checks if the view model has any validation errors:
public class ViewModel : INotifyPropertyChanged, INotifyDataErrorInfo
{
// Usage example property which validates its value
// before applying it using a Lambda expression.
// Example uses System.ValueTuple.
private string userInput;
public string UserInput
{
get => this.userInput;
set
{
// Use Lambda
if (ValidateProperty(value, newValue => newValue.StartsWith("#") ? (true, string.Empty) : (false, "Value must start with '#'.")))
{
this.userInput = value;
OnPropertyChanged();
}
}
}
// Alternative usage example property which validates its value
// before applying it using a Method group.
// Example uses System.ValueTuple.
private string userInputAlternativeValidation;
public string UserInputAlternativeValidation
{
get => this.userInputAlternativeValidation;
set
{
// Use Method group
if (ValidateProperty(value, AlternativeValidation))
{
this.userInputAlternativeValidation = value;
OnPropertyChanged();
}
}
}
private (bool IsValid, string ErrorMessage) AlternativeValidation(string value)
{
return value.StartsWith("#")
? (true, string.Empty)
: (false, "Value must start with '#'.");
}
// Alternative usage example property which validates its value
// before applying it using a ValidationAttribute.
private string userInputAttributeValidation;
[Required(ErrorMessage = "Value is required.")]
public string UserInputAttributeValidation
{
get => this.userInputAttributeValidation;
set
{
// Use only the attribute (can be combined with a Lambda or Method group)
if (ValidateProperty(value))
{
this.userInputAttributeValidation = value;
OnPropertyChanged();
}
}
}
private bool TrySave()
{
if (this.HasErrors)
{
return false;
}
// View model has no errors. Save data.
return true;
}
// Constructor
public ViewModel()
{
this.Errors = new Dictionary<string, List<string>>();
}
// Example uses System.ValueTuple
public bool ValidateProperty(object value, Func<object, (bool IsValid, string ErrorMessage)> validationDelegate = null, [CallerMemberName] string propertyName = null)
{
// Clear previous errors of the current property to be validated
this.Errors.Remove(propertyName);
OnErrorsChanged(propertyName);
// First validate using the delegate
(bool IsValid, string ErrorMessage) validationResult = validationDelegate?.Invoke(value) ?? (true, string.Empty);
if (!validationResult.IsValid)
{
AddError(propertyName, validationResult.ErrorMessage);
}
// Check if property is decorated with validation attributes
// using reflection
IEnumerable<Attribute> validationAttributes = GetType()
.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
?.GetCustomAttributes(typeof(ValidationAttribute)) ?? new List<Attribute>();
// Validate attributes if present
if (validationAttributes.Any())
{
var validationResults = new List<ValidationResult>();
if (!Validator.TryValidateProperty(value, new ValidationContext(this, null, null) { MemberName = propertyName }, validationResults))
{
foreach (ValidationResult attributeValidationResult in validationResults)
{
AddError(propertyName, attributeValidationResult.ErrorMessage);
}
validationResult = (false, string.Empty);
}
}
return validationResult.IsValid;
}
// Adds the specified error to the errors collection if it is not
// already present, inserting it in the first position if 'isWarning' is
// false. Raises the ErrorsChanged event if the Errors collection changes.
public void AddError(string propertyName, string errorMessage, bool isWarning = false)
{
if (!this.Errors.TryGetValue(propertyName, out List<string> propertyErrors))
{
propertyErrors = new List<string>();
this.Errors[propertyName] = propertyErrors;
}
if (!propertyErrors.Contains(errorMessage))
{
if (isWarning)
{
// Move warnings to the end
propertyErrors.Add(errorMessage);
}
else
{
propertyErrors.Insert(0, errorMessage);
}
OnErrorsChanged(propertyName);
}
}
public bool PropertyHasErrors(string propertyName) => this.Errors.TryGetValue(propertyName, out List<string> propertyErrors) && propertyErrors.Any();
#region INotifyDataErrorInfo implementation
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
// Returns all errors of a property. If the argument is 'null' instead of the property's name,
// then the method will return all errors of all properties.
public System.Collections.IEnumerable GetErrors(string propertyName)
=> string.IsNullOrWhiteSpace(propertyName)
? this.Errors.SelectMany(entry => entry.Value)
: this.Errors.TryGetValue(propertyName, out IEnumerable<string> errors)
? errors
: new List<string>();
// Returns if the view model has any invalid property
public bool HasErrors => this.Errors.Any();
#endregion
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnErrorsChanged(string propertyName)
{
this.ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
}
// Maps a property name to a list of errors that belong to this property
private Dictionary<String, List<String>> Errors { get; }
}
This link contains an explanation and links to more examples: How to add validation to view model properties or how to implement INotifyDataErrorInfo

How do I disable IDataErrorInfo & DataAnnotations Validation on load

I am writing a WPF application with a MVVM approach, and I am using IDataErrorInfo and DataAnnotations to validate input data. Just like this:
ViewModel
/// <summary>
/// User
/// </summary>
[Required(ErrorMessage = "not blank")]
[StringLength(20, MinimumLength = 6, ErrorMessage = "between 6 and 20")]
public string UserID
{
get
{
return _adminInfoModel.UserID;
}
set
{
if (_adminInfoModel.UserID != value)
{
_adminInfoModel.UserID = value;
OnPropertyChanged("UserID");
}
}
}
/// <summary>
/// Name
/// </summary>
[Required(ErrorMessage = "not blank")]
[StringLength(100, ErrorMessage = "less than 100 character")]
public string Name
{
get
{
return _adminInfoModel.Name;
}
set
{
if (_adminInfoModel.Name != value)
{
_adminInfoModel.Name = value;
OnPropertyChanged("Name");
}
}
}
//many properties here....
//implement the IDataErrorInfo interface
public string this[string columnName]
{
get
{
ValidationContext vc = new ValidationContext(this, null, null);
vc.MemberName = columnName;
List<ValidationResult> results = new List<ValidationResult>();
bool result = Validator.TryValidateProperty(this.GetType().GetProperty(columnName).GetValue(this, null), vc, results);
if (results.Count > 0)
{
return results[0].ErrorMessage;
}
return string.Empty;
}
}
View:
<TextBox Name="UserIDTB" Text="{Binding UserID, UpdateSourceTrigger=LostFocus, Mode=TwoWay, ValidatesOnDataErrors=True}" />
<TextBox Name="NameTB" Text="{Binding Name, ValidatesOnDataErrors=True}" />
The Problem is:
When I open this view, Because the ViewModel implement the IDataErrorInfo interface, the application will validate the properties at once. Some properties use RequiredAttribute validation. So the application will point out blank error when opening the windows immediately. Like this:
How can the application skip validating properties when open the windows at once? Another way, How can the application validate the RequiredAttribute when click the submit button?
Thank you very much!!
This is always little bit tricky. There are two approaches:
Foreach property create another boolean field, or dictionary entry to indicate, whether the property should be validated. In setter of each property, set the field to true. if the property has not been set, then don't return an error. You will also need validate method, that will validate all properties.
Use INotifyDataErrorInfo, you notify the view when an error occurs :
here is example:
public class MyViewModel : ValidatableBase
{
[Required]
public string SomeProperty
{
get { return _someProperty; }
set { SetProperty(ref _someProperty, value); }
}
}
public abstract class ValidatableBase : BindableBase, INotifyDataErrorInfo
{
private readonly Dictionary<string, string> _propertyErrors = new Dictionary<string, string>();
protected override bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
var result = base.SetProperty(ref storage, value, propertyName);
var error = ValidateProperty(propertyName, value);
SetError(propertyName, error);
return result;
}
private void SetError(string propertyName, string error, bool notify = false)
{
string existingError;
_propertyErrors.TryGetValue(propertyName, out existingError);
if (error == null)
{
if (existingError != null) _propertyErrors.Remove(propertyName);
}
else
{
_propertyErrors[propertyName] = error;
}
if (existingError != error)
{
OnErrorsChanged(propertyName);
}
}
public virtual bool Validate()
{
var properties = TypeDescriptor.GetProperties(this);
foreach (PropertyDescriptor property in properties)
{
var error = ValidateProperty(property.Name, property.GetValue(this));
SetError(property.Name, error, true);
}
return HasErrors;
}
public void Validate(string propertyName, object value)
{
var error = ValidateProperty(propertyName, value);
SetError(propertyName, error, true);
}
protected virtual string ValidateProperty(string propertyName, object value)
{
if (propertyName == null) throw new ArgumentNullException("propertyName");
var validationContext = new ValidationContext(this);
validationContext.MemberName = propertyName;
var validationResults = new List<ValidationResult>();
if (Validator.TryValidateProperty(value, validationContext, validationResults))
{
return null;
}
return validationResults[0].ErrorMessage;
}
protected virtual void OnErrorsChanged(string propertyName)
{
var handler = ErrorsChanged;
if (handler != null) handler(this, new DataErrorsChangedEventArgs(propertyName));
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public System.Collections.IEnumerable GetErrors(string propertyName)
{
if (string.IsNullOrEmpty(propertyName)) yield break;
string existingError;
if (_propertyErrors.TryGetValue(propertyName, out existingError))
{
yield return existingError;
}
}
public bool HasErrors
{
get { return _propertyErrors.Count > 0; }
}
}
}
Implement INotifyDataErrorInfo in your base view model and add an isValidating bool field. In your GetErrors(string propName) implementation, first check isValidating and return early if false.
You should also add a Validate() method that sets isValidating to true and kicks off a full object validation with Validator.TryValidateObject(). Call Validate() when the user clicks OK and from then on all property modifications will update validation.

Binding to DynamicObjects in XAML in WinRT

I have an ObservableCollection<dynamic> class and XAML refuses to bind to the properties on the contained objects.
I know I read somewhere that XAML supports dynamic and DyanmicObject so I'm mightily confused on why this is not working. Other questions, such as this one, were spectacularly un-helpful:
Can i bind against a DynamicObject in WinRT / Windows 8 Store Apps
I get this error at runtime (and in the designer at design time when hovering over my {Bindings):
Error: BindingExpression path error: 'DisplayName' property not found on 'PremiseMetro.Light, PremiseMetro, ... BindingExpression: Path='DisplayName' DataItem='PremiseMetro.Light, PremiseMetro, ... target element is 'Windows.UI.Xaml.Controls.TextBlock' (Name='null'); target property is 'Text' (type 'String')
Please help!
Thanks.
A test ObservableObject class:
class Light : DynamicObject, INotifyPropertyChanged {
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
public override bool TryGetMember(GetMemberBinder binder, out object result) {
string name = binder.Name;
result = null;
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
object prop;
if (_properties.TryGetValue(name, out prop)) {
result = prop;
return true;
}
return false;
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(SetMemberBinder binder, object value) {
string name = binder.Name;
_properties[name] = value;
if (CoreApplication.MainView.CoreWindow.Dispatcher.HasThreadAccess)
OnPropertyChanged(name);
else
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal, () => OnPropertyChanged(name));
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
public object GetMember(string propName) {
var binder = Binder.GetMember(CSharpBinderFlags.None,
propName, GetType(),
new List<CSharpArgumentInfo> {
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var callsite = CallSite<Func<CallSite, object, object>>.Create(binder);
return callsite.Target(callsite, this);
}
/// <summary>
/// Sets the value of a property.
/// </summary>
/// <param name="propertyName">Name of property</param>
/// <param name="val">New value</param>
/// <param name="fromServer">If true, will not try to update server.</param>
public void SetMember(String propertyName, object val) {
var binder = Binder.SetMember(CSharpBinderFlags.None,
propertyName, GetType(),
new List<CSharpArgumentInfo> {
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null)
});
var callsite = CallSite<Func<CallSite, object, object, object>>.Create(binder);
callsite.Target(callsite, this, val);
}
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
A test in my MainViewMOdel constructor:
Light light = new Light();
((dynamic) light).DisplayName = "Test Light";
((dynamic) light).Brightness= "27%";
((dynamic) light).PowerState= false;
Lights = new ObservableCollection<dynamic> {
light
};
My test XAML:
<Grid Margin="10" Width="1000" VerticalAlignment="Stretch">
<ListBox x:Name="GDOList" ItemsSource="{Binding Path=Lights}" >
<ListBox.ItemTemplate>
<DataTemplate >
<Grid Margin="6">
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=DisplayName}" Margin="5" />
<TextBlock Text="{Binding Path=PowerState}" Margin="5" />
<TextBlock Text="{Binding Path=Brightness}" Margin="5" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Page>
Can you try changing your ViewModel to:
dynamic light = new ExpandoObject();
light.DisplayName = "Test Light";
light.Brightness = "27%";
light.PowerState = false;
var objs = new ObservableCollection<dynamic> { light };
and see if that works in your lib correctly?
Short Answer: no, binding to an dynamic property on an instance of DynamicObject in UWP is not supported.
However, there do exist some way to do similar thing, with a ICustomPropertyProvider.
Assume your class looks something like this:
public class SomeClass : DynamicObject, INotifyPropertyChanged {
private string _StaticStringProperty;
public string StaticStringProperty { get => _StaticStringProperty; set => SetField(ref _StaticStringProperty, value); }
private Dictionary<string, object> _DynamicProperties = new Dictionary<string, object>();
public override IEnumerable<string> GetDynamicMemberNames()
{
yield return "DynamicStringProperty";
yield return "DynamicIntegerProperty";
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _DynamicProperties.GetValueOrDefault(binder.Name, null);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_DynamicProperties[binder.Name] = value;
RaisePropertyChanged(binder.Name);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
protected bool SetField<T>(ref T target, T value, [CallerMemberName]string caller = null)
{
if (EqualityComparer<T>.Default.Equals(target, value))
return false;
target = value;
RaisePropertyChanged(caller);
return true;
}
}
now let it implement ICustomPropertyProvider:
public class SomeClass : DynamicObject, ICustomPropertyProvider, INotifyPropertyChanged {
...
public Type Type => GetType();
public string GetStringRepresentation() => ToString();
public ICustomProperty GetCustomProperty(string name)
{
switch (name)
{
// the caveat is that you have to provide all the static properties, too...
case nameof(StaticStringProperty):
return new DynamicCustomProperty<SomeClass, string>()
{
Name = name,
Getter = (target) => target.StaticStringProperty,
Setter = (target, value) => target.StaticStringProperty = value,
};
case "DynamicStringProperty":
return new DynamicCustomProperty<SomeClass, string>()
{
Name = name,
Getter = (target) => target.DynamicStringProperty,
Setter = (target, value) => target.DynamicStringProperty = value,
};
case "DynamicIntegerProperty":
return new DynamicCustomProperty<SomeClass, int>()
{
Name = name,
Getter = (target) => target.DynamicIntegerProperty,
Setter = (target, value) => target.DynamicIntegerProperty = value,
};
}
}
throw new NotImplementedException();
}
...
}
and be able to provide the DynamicCustomProperty:
public class DynamicCustomProperty<TOwner, TValue> : ICustomProperty
{
public Func<dynamic, TValue> Getter { get; set; }
public Action<dynamic, TValue> Setter { get; set; }
public Func<dynamic, object, TValue> IndexGetter { get; set; }
public Action<dynamic, object, TValue> IndexSetter { get; set; }
public object GetValue(object target) => Getter.Invoke(target);
public void SetValue(object target, object value) => Setter.Invoke(target, (TValue)value);
public object GetIndexedValue(object target, object index) => IndexGetter.Invoke(target, index);
public void SetIndexedValue(object target, object value, object index) => IndexSetter.Invoke(target, index, (TValue)value);
public bool CanRead => Getter != null || IndexGetter != null;
public bool CanWrite => Setter != null || IndexSetter != null;
public string Name { get; set; }
public Type Type => typeof(TValue);
}
finally we're able to bind them in XAML:
<TextBox Header="Static String" Text="{Binding StaticStringProperty, Mode=TwoWay}"/>
<TextBox Header="Dynamic String" Text="{Binding DynamicStringProperty, Mode=TwoWay}"/>
<TextBox Header="Dynamic Integer" Text="{Binding DynamicIntegerProperty, Mode=TwoWay}"/>

Is there a way in C# to turn the property into a string containing it's name?

I'd like to get a string representation given a property. This way I can use this string for NotifyPropertyChanged and stil be ok after a refactoring of the property's name.
EDIT: I'm using .NET 4.0
UPDATE: I'd also like to have the name available for DependencyProprtys, i.e. I need the value during static variable assignment time.
Same sample code to explain:
// actual code
private int prop = 42;
public int Prop
{
get
{
return prop;
}
set
{
prop = value;
NotifyPropertyChanged("Prop"); // I'd like to replace the hard-coded string here
}
}
// code as I'd like it to be
private int propNew = 42;
private static readonly string PropNewName = GainStringFromPropertySomeHow(PropNew); // should be "PropNew"
public int PropNew
{
get
{
return propNew;
}
set
{
propNew = value;
NotifyPropertyChanged(PropNewName); // <== will remain correct even if PropNew name is changed
}
}
After refactoring:
private int prop = 42;
public int PropNameChanged
{
get
{
return prop;
}
set
{
prop = value;
NotifyPropertyChanged("Prop"); // oops
}
}
private int propNew = 42;
private static readonly string PropNewName = GainStringFromPropertySomeHow(PropNewNameChanged); // should be "PropNewNameChanged"
public int PropNewNameChanged
{
get
{
return propNew;
}
set
{
propNew = value;
NotifyPropertyChanged(PropNewName); // still correct
}
}
I think this could be helpfull:
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Source and more explanation: http://msdn.microsoft.com/de-de/library/system.componentmodel.inotifypropertychanged.aspx
And here:
http://msdn.microsoft.com/de-de/library/system.runtime.compilerservices.callermembernameattribute.aspx
If you're not yet on .Net 4.5 and thus cannot use CallerMemberName, you can use this approach: https://stackoverflow.com/a/3191598/869250
This is a duplicate of How to get current property name via reflection?
So you can just do this
NotifyPropertyChanged(MethodBase.GetCurrentMethod().Name);
I found a solution here on Stackoverflow:
https://stackoverflow.com/a/672212/1254743
and
https://stackoverflow.com/a/2820759/1254743
The resulting code:
public static class PropertyNameExtractor
{
/// <summary>
/// Usage: PropertyNameExtractor.ExposeProperty(() => this.YourProperty)
/// yields: "YourProperty"
/// </summary>
public static string ExposeProperty<T>(Expression<Func<T>> property)
{
var expression = GetMemberInfo(property);
return expression.Member.Name;
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
}
In my class:
class MyClass: INotifyPropertyChanged
{
public MyClass()
{
this.nameOf_MyProperty = PropertyNameExtractor.ExposeProperty(() => this.MyProperty);
}
private readonly string nameOf_MyProperty;
private int myProperty = 42 ;
public int MyProperty
{
get
{
return myProperty;
}
set
{
myProperty= value;
NotifyPropertyChanged(nameOf_MyProperty);
}
}
private void NotifyPropertyChanged(String PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
}

Implementing INotifyPropertyChanged - does a better way exist?

Microsoft should have implemented something snappy for INotifyPropertyChanged, like in the automatic properties, just specify {get; set; notify;}
I think it makes a lot of sense to do it. Or are there any complications to do it?
Can we ourselves implement something like 'notify' in our properties. Is there a graceful solution for implementing INotifyPropertyChanged in your class or the only way to do it is by raising the PropertyChanged event in each property.
If not can we write something to auto-generate the piece of code to raise PropertyChanged event?
Without using something like postsharp, the minimal version I use uses something like:
public class Data : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
// props
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
}
Each property is then just something like:
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
which isn't huge; it can also be used as a base-class if you want. The bool return from SetField tells you if it was a no-op, in case you want to apply other logic.
or even easier with C# 5:
protected bool SetField<T>(ref T field, T value,
[CallerMemberName] string propertyName = null)
{...}
which can be called like this:
set { SetField(ref name, value); }
with which the compiler will add the "Name" automatically.
C# 6.0 makes the implementation easier:
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
...and now with C#7:
protected void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
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 => name;
set => SetField(ref name, value);
}
And, with C# 8 and Nullable reference types, it would look like this:
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private string name;
public string Name
{
get => name;
set => SetField(ref name, value);
}
As of .Net 4.5 there is finally an easy way to do this.
.Net 4.5 introduces a new Caller Information Attributes.
private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
// make sure only to call this if the value actually changes
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(caller));
}
}
It's probably a good idea to add a comparer to the function as well.
EqualityComparer<T>.Default.Equals
More examples here and here
Also see Caller Information (C# and Visual Basic)
I really like Marc's solution, but I think it can be slightly improved to avoid using a "magic string" (which doesn't support refactoring). Instead of using the property name as a string, it's easy to make it a lambda expression :
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, () => Name); }
}
Just add the following methods to Marc's code, it will do the trick :
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(selectorExpression);
return true;
}
BTW, this was inspired by this blog post.
There's also Fody which has a AddINotifyPropertyChangedInterface add-in, which lets you write this:
[AddINotifyPropertyChangedInterface]
public class Person
{
public string GivenNames { get; set; }
public string FamilyName { get; set; }
}
...and at compile time injects property changed notifications.
I think people should pay a little more attention to performance; it really does impact the UI when there are a lot of objects to be bound (think of a grid with 10,000+ rows), or if the object's value changes frequently (real-time monitoring app).
I took various implementation found here and elsewhere and did a comparison; check it out perfomance comparison of INotifyPropertyChanged implementations.
Here is a peek at the result
I introduce a Bindable class in my blog at http://timoch.com/blog/2013/08/annoyed-with-inotifypropertychange/
Bindable uses a dictionary as a property bag. 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
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
protected T Get<T>([CallerMemberName] string name = null) {
Debug.Assert(name != null, "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>
/// <remarks>Use this overload when implicitly naming the property</remarks>
protected void Set<T>(T value, [CallerMemberName] string name = null) {
Debug.Assert(name != null, "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) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
It can be used like this:
public class Contact : Bindable {
public string FirstName {
get { return Get<string>(); }
set { Set(value); }
}
}
I haven't actually had a chance to try this myself yet, but next time I'm setting up a project with a big requirement for INotifyPropertyChanged I'm intending on writing a Postsharp attribute that will inject the code at compile time. Something like:
[NotifiesChange]
public string FirstName { get; set; }
Will become:
private string _firstName;
public string FirstName
{
get { return _firstname; }
set
{
if (_firstname != value)
{
_firstname = value;
OnPropertyChanged("FirstName")
}
}
}
I'm not sure if this will work in practice and I need to sit down and try it out, but I don't see why not. I may need to make it accept some parameters for situations where more than one OnPropertyChanged needs to be triggered (if, for example, I had a FullName property in the class above)
Currently I'm using a custom template in Resharper, but even with that I'm getting fed up of all my properties being so long.
Ah, a quick Google search (which I should have done before I wrote this) shows that at least one person has done something like this before here. Not exactly what I had in mind, but close enough to show that the theory is good.
Yes, better way certainly exists.
Here it is:
Step by step tutorial shrank by me, based on this useful article.
Create new project
Install castle core package into the project
Install-Package Castle.Core
Install mvvm light libraries only
Install-Package MvvmLightLibs
Add two classes in project:
NotifierInterceptor
public class NotifierInterceptor : IInterceptor
{
private PropertyChangedEventHandler handler;
public static Dictionary<String, PropertyChangedEventArgs> _cache =
new Dictionary<string, PropertyChangedEventArgs>();
public void Intercept(IInvocation invocation)
{
switch (invocation.Method.Name)
{
case "add_PropertyChanged":
handler = (PropertyChangedEventHandler)
Delegate.Combine(handler, (Delegate)invocation.Arguments[0]);
invocation.ReturnValue = handler;
break;
case "remove_PropertyChanged":
handler = (PropertyChangedEventHandler)
Delegate.Remove(handler, (Delegate)invocation.Arguments[0]);
invocation.ReturnValue = handler;
break;
default:
if (invocation.Method.Name.StartsWith("set_"))
{
invocation.Proceed();
if (handler != null)
{
var arg = retrievePropertyChangedArg(invocation.Method.Name);
handler(invocation.Proxy, arg);
}
}
else invocation.Proceed();
break;
}
}
private static PropertyChangedEventArgs retrievePropertyChangedArg(String methodName)
{
PropertyChangedEventArgs arg = null;
_cache.TryGetValue(methodName, out arg);
if (arg == null)
{
arg = new PropertyChangedEventArgs(methodName.Substring(4));
_cache.Add(methodName, arg);
}
return arg;
}
}
ProxyCreator
public class ProxyCreator
{
public static T MakeINotifyPropertyChanged<T>() where T : class, new()
{
var proxyGen = new ProxyGenerator();
var proxy = proxyGen.CreateClassProxy(
typeof(T),
new[] { typeof(INotifyPropertyChanged) },
ProxyGenerationOptions.Default,
new NotifierInterceptor()
);
return proxy as T;
}
}
Create your view model, for example:
-
public class MainViewModel
{
public virtual string MainTextBox { get; set; }
public RelayCommand TestActionCommand
{
get { return new RelayCommand(TestAction); }
}
public void TestAction()
{
Trace.WriteLine(MainTextBox);
}
}
Put bindings into xaml:
<TextBox Text="{Binding MainTextBox}" ></TextBox>
<Button Command="{Binding TestActionCommand}" >Test</Button>
Put line of code in code-behind file MainWindow.xaml.cs like this:
DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();
Enjoy.
Attention!!! All bounded properties should be decorated with
keyword virtual because they used by castle proxy for overriding.
A very AOP-like approach is to inject the INotifyPropertyChanged stuff onto an already instantiated object on the fly. You can do this with something like Castle DynamicProxy. Here is an article that explains the technique:
Adding INotifyPropertyChanged to an existing object
It's 2022. Now there's an official solution.
Use the MVVM source generators in Microsoft MVVM Toolkit.
This
[ObservableProperty]
private string? name;
will generate:
private string? name;
public string? Name
{
get => name;
set
{
if (!EqualityComparer<string?>.Default.Equals(name, value))
{
OnNameChanging(value);
OnPropertyChanging();
name = value;
OnNameChanged(value);
OnPropertyChanged();
}
}
}
// Property changing / changed listener
partial void OnNameChanging(string? value);
partial void OnNameChanged(string? value);
protected void OnPropertyChanging([CallerMemberName] string? propertyName = null)
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
It supports .NET standard 2.0 and .NET >= 5.0.
Look here : http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx
It's written in German, but you can download the ViewModelBase.cs. All the comments in the cs-File are written in English.
With this ViewModelBase-Class it is possible to implement bindable properties similar to the well known Dependency Properties :
public string SomeProperty
{
get { return GetValue( () => SomeProperty ); }
set { SetValue( () => SomeProperty, value ); }
}
Let me introduce my own approach called Yappi.
It belongs to Runtime proxy|derived class generators, adding new functionality to an existing object or type, like Caste Project's Dynamic Proxy.
It allows to implement INotifyPropertyChanged once in base class, and then declare derived classes in following style, still supporting INotifyPropertyChanged for new properties:
public class Animal:Concept
{
protected Animal(){}
public virtual string Name { get; set; }
public virtual int Age { get; set; }
}
Complexity of derived class or proxy construction can be hidden behind the following line:
var animal = Concept.Create<Animal>.New();
And all INotifyPropertyChanged implementation work can be done like this:
public class Concept:INotifyPropertyChanged
{
//Hide constructor
protected Concept(){}
public static class Create<TConcept> where TConcept:Concept
{
//Construct derived Type calling PropertyProxy.ConstructType
public static readonly Type Type = PropertyProxy.ConstructType<TConcept, Implementation<TConcept>>(new Type[0], true);
//Create constructing delegate calling Constructor.Compile
public static Func<TConcept> New = Constructor.Compile<Func<TConcept>>(Type);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
var caller = PropertyChanged;
if(caller!=null)
{
caller(this, eventArgs);
}
}
//define implementation
public class Implementation<TConcept> : DefaultImplementation<TConcept> where TConcept:Concept
{
public override Func<TBaseType, TResult> OverrideGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
{
return PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
}
/// <summary>
/// Overriding property setter implementation.
/// </summary>
/// <typeparam name="TBaseType">Base type for implementation. TBaseType must be TConcept, and inherits all its constraints. Also TBaseType is TDeclaringType.</typeparam>
/// <typeparam name="TDeclaringType">Type, declaring property.</typeparam>
/// <typeparam name="TConstructedType">Constructed type. TConstructedType is TDeclaringType and TBaseType.</typeparam>
/// <typeparam name="TResult">Type of property.</typeparam>
/// <param name="property">PropertyInfo of property.</param>
/// <returns>Delegate, corresponding to property setter implementation.</returns>
public override Action<TBaseType, TResult> OverrideSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
{
//This code called once for each declared property on derived type's initialization.
//EventArgs instance is shared between all events for each concrete property.
var eventArgs = new PropertyChangedEventArgs(property.Name);
//get delegates for base calls.
Action<TBaseType, TResult> setter = PropertyImplementation<TBaseType, TDeclaringType>.GetSetter<TResult>(property.Name);
Func<TBaseType, TResult> getter = PropertyImplementation<TBaseType, TDeclaringType>.GetGetter<TResult>(property.Name);
var comparer = EqualityComparer<TResult>.Default;
return (pthis, value) =>
{//This code executes each time property setter is called.
if (comparer.Equals(value, getter(pthis))) return;
//base. call
setter(pthis, value);
//Directly accessing Concept's protected method.
pthis.OnPropertyChanged(eventArgs);
};
}
}
}
It is fully safe for refactoring, uses no reflection after type construction and fast enough.
Whilst there are obviously lots of ways to do this, with the exception of the AOP magic answers, none of the answers seem to look at setting a Model's property directly from the view model without having a local field to reference.
The issue is you can't reference a property. However, you can use an Action to set that property.
protected bool TrySetProperty<T>(Action<T> property, T newValue, T oldValue, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(oldValue, newValue))
{
return false;
}
property(newValue);
RaisePropertyChanged(propertyName);
return true;
}
This can be used like the following code extract.
public int Prop {
get => model.Prop;
set => TrySetProperty(x => model.Prop = x, value, model.Prop);
}
Check out this BitBucket repo for a full implementation of the method and a few different ways of achieving the same result, including a method that uses LINQ and a method that uses reflection. Do note that these methods are slower performance wise.
All these answer are very nice.
My solution is using the code snippets to do the job.
This uses the simplest call to PropertyChanged event.
Save this snippet and use it as you use 'fullprop' snippet.
the location can be found at 'Tools\Code Snippet Manager...' menu at Visual Studio.
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>inotifypropfull</Title>
<Shortcut>inotifypropfull</Shortcut>
<HelpUrl>http://ofirzeitoun.wordpress.com/</HelpUrl>
<Description>Code snippet for property and backing field with notification</Description>
<Author>Ofir Zeitoun</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<ToolTip>Property type</ToolTip>
<Default>int</Default>
</Literal>
<Literal>
<ID>property</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
<Literal>
<ID>field</ID>
<ToolTip>The variable backing this property</ToolTip>
<Default>myVar</Default>
</Literal>
</Declarations>
<Code Language="csharp">
<![CDATA[private $type$ $field$;
public $type$ $property$
{
get { return $field$;}
set {
$field$ = value;
var temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs("$property$"));
}
}
}
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
You can modify the call as you like (to use the above solutions)
Based on the answer by Thomas which was adapted from an answer by Marc I've turned the reflecting property changed code into a base class:
public abstract class PropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
var me = selectorExpression.Body as MemberExpression;
// Nullable properties can be nested inside of a convert function
if (me == null)
{
var ue = selectorExpression.Body as UnaryExpression;
if (ue != null)
me = ue.Operand as MemberExpression;
}
if (me == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(me.Member.Name);
}
protected void SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression, params Expression<Func<object>>[] additonal)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
OnPropertyChanged(selectorExpression);
foreach (var item in additonal)
OnPropertyChanged(item);
}
}
Usage is the same as Thomas' answer except that you can pass additional properties to notify for. This was necessary to handle calculated columns which need to be refreshed in a grid.
private int _quantity;
private int _price;
public int Quantity
{
get { return _quantity; }
set { SetField(ref _quantity, value, () => Quantity, () => Total); }
}
public int Price
{
get { return _price; }
set { SetField(ref _price, value, () => Price, () => Total); }
}
public int Total { get { return _price * _quantity; } }
I have this driving a collection of items stored in a BindingList exposed via a DataGridView. It has eliminated the need for me to do manual Refresh() calls to the grid.
I created an Extension Method in my base Library for reuse:
public static class INotifyPropertyChangedExtensions
{
public static bool SetPropertyAndNotify<T>(this INotifyPropertyChanged sender,
PropertyChangedEventHandler handler, ref T field, T value,
[CallerMemberName] string propertyName = "",
EqualityComparer<T> equalityComparer = null)
{
bool rtn = false;
var eqComp = equalityComparer ?? EqualityComparer<T>.Default;
if (!eqComp.Equals(field,value))
{
field = value;
rtn = true;
if (handler != null)
{
var args = new PropertyChangedEventArgs(propertyName);
handler(sender, args);
}
}
return rtn;
}
}
This works with .Net 4.5 because of CallerMemberNameAttribute.
If you want to use it with an earlier .Net version you have to change the method declaration from: ...,[CallerMemberName] string propertyName = "", ... to ...,string propertyName, ...
Usage:
public class Dog : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string _name;
public string Name
{
get { return _name; }
set
{
this.SetPropertyAndNotify(PropertyChanged, ref _name, value);
}
}
}
I keep this around as a snippet. C# 6 adds some nice syntax for invoking the handler.
// INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void Set<T>(ref T property, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(property, value) == false)
{
property = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have just found ActiveSharp - Automatic INotifyPropertyChanged, I have yet to use it, but it looks good.
To quote from it's web site...
Send property change notifications
without specifying property name as a
string.
Instead, write properties like this:
public int Foo
{
get { return _foo; }
set { SetValue(ref _foo, value); } // <-- no property name here
}
Note that there is no need to include the name of the property as a string. ActiveSharp reliably and correctly figures that out for itself. It works based on the fact that your property implementation passes the backing field (_foo) by ref. (ActiveSharp uses that "by ref" call to identify which backing field was passed, and from the field it identifies the property).
If you are using dynamics in .NET 4.5 you don't need to worry about INotifyPropertyChanged.
dynamic obj = new ExpandoObject();
obj.Name = "John";
if Name is bound to some control it just works fine.
Another combined solution is using StackFrame:
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T field, T value)
{
MethodBase method = new StackFrame(1).GetMethod();
field = value;
Raise(method.Name.Substring(4));
}
protected void Raise(string propertyName)
{
var temp = PropertyChanged;
if (temp != null)
{
temp(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Usage:
public class TempVM : BaseViewModel
{
private int _intP;
public int IntP
{
get { return _intP; }
set { Set<int>(ref _intP, value); }
}
}
I resolved in This Way (it's a little bit laboriouse, but it's surely the faster in runtime).
In VB (sorry, but I think it's not hard translate it in C#), I make this substitution with RE:
(?<Attr><(.*ComponentModel\.)Bindable\(True\)>)( |\r\n)*(?<Def>(Public|Private|Friend|Protected) .*Property )(?<Name>[^ ]*) As (?<Type>.*?)[ |\r\n](?![ |\r\n]*Get)
with:
Private _${Name} As ${Type}\r\n${Attr}\r\n${Def}${Name} As ${Type}\r\nGet\r\nReturn _${Name}\r\nEnd Get\r\nSet (Value As ${Type})\r\nIf _${Name} <> Value Then \r\n_${Name} = Value\r\nRaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("${Name}"))\r\nEnd If\r\nEnd Set\r\nEnd Property\r\n
This transofrm all code like this:
<Bindable(True)>
Protected Friend Property StartDate As DateTime?
In
Private _StartDate As DateTime?
<Bindable(True)>
Protected Friend Property StartDate As DateTime?
Get
Return _StartDate
End Get
Set(Value As DateTime?)
If _StartDate <> Value Then
_StartDate = Value
RaiseEvent PropertyChange(Me, New ComponentModel.PropertyChangedEventArgs("StartDate"))
End If
End Set
End Property
And If I want to have a more readable code, I can be the opposite just making the following substitution:
Private _(?<Name>.*) As (?<Type>.*)[\r\n ]*(?<Attr><(.*ComponentModel\.)Bindable\(True\)>)[\r\n ]*(?<Def>(Public|Private|Friend|Protected) .*Property )\k<Name> As \k<Type>[\r\n ]*Get[\r\n ]*Return _\k<Name>[\r\n ]*End Get[\r\n ]*Set\(Value As \k<Type>\)[\r\n ]*If _\k<Name> <> Value Then[\r\n ]*_\k<Name> = Value[\r\n ]*RaiseEvent PropertyChanged\(Me, New (.*ComponentModel\.)PropertyChangedEventArgs\("\k<Name>"\)\)[\r\n ]*End If[\r\n ]*End Set[\r\n ]*End Property
With
${Attr} ${Def} ${Name} As ${Type}
I throw to replace the IL code of the set method, but I can't write a lot of compiled code in IL... If a day I write it, I'll say you!
Here is a Unity3D or non-CallerMemberName version of NotifyPropertyChanged
public abstract class Bindable : MonoBehaviour, INotifyPropertyChanged
{
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
private static readonly StackTrace stackTrace = new StackTrace();
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Resolves a Property's name from a Lambda Expression passed in.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="property"></param>
/// <returns></returns>
internal string GetPropertyName<T>(Expression<Func<T>> property)
{
var expression = (MemberExpression) property.Body;
var propertyName = expression.Member.Name;
Debug.AssertFormat(propertyName != null, "Bindable Property shouldn't be null!");
return propertyName;
}
#region Notification Handlers
/// <summary>
/// Notify's all other objects listening that a value has changed for nominated propertyName
/// </summary>
/// <param name="propertyName"></param>
internal void NotifyOfPropertyChange(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Notifies subscribers of the property change.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">The property expression.</param>
internal void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
{
var propertyName = GetPropertyName(property);
NotifyOfPropertyChange(propertyName);
}
/// <summary>
/// Raises the <see cref="PropertyChanged" /> event directly.
/// </summary>
/// <param name="e">The <see cref="PropertyChangedEventArgs" /> instance containing the event data.</param>
internal void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
#endregion
#region Getters
/// <summary>
/// Gets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
internal T Get<T>(Expression<Func<T>> property)
{
var propertyName = GetPropertyName(property);
return Get<T>(GetPropertyName(property));
}
/// <summary>
/// Gets the value of a property automatically based on its caller.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
internal T Get<T>()
{
var name = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
return Get<T>(name);
}
/// <summary>
/// Gets the name of a property based on a string.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
internal T Get<T>(string name)
{
object value = null;
if (_properties.TryGetValue(name, out value))
return value == null ? default(T) : (T) value;
return default(T);
}
#endregion
#region Setters
/// <summary>
/// Sets the value of a property whilst automatically looking up its caller name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
internal void Set<T>(T value)
{
var propertyName = stackTrace.GetFrame(1).GetMethod().Name.Substring(4); // strips the set_ from name;
Set(value, propertyName);
}
/// <summary>
/// Sets the value of a property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="name"></param>
internal void Set<T>(T value, string propertyName)
{
Debug.Assert(propertyName != null, "name != null");
if (Equals(value, Get<T>(propertyName)))
return;
_properties[propertyName] = value;
NotifyOfPropertyChange(propertyName);
}
/// <summary>
/// Sets the value of a property based off an Expression (()=>FieldName)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="property"></param>
internal void Set<T>(T value, Expression<Func<T>> property)
{
var propertyName = GetPropertyName(property);
Debug.Assert(propertyName != null, "name != null");
if (Equals(value, Get<T>(propertyName)))
return;
_properties[propertyName] = value;
NotifyOfPropertyChange(propertyName);
}
#endregion
}
This code enables you to write property backing fields like this:
public string Text
{
get { return Get<string>(); }
set { Set(value); }
}
Furthermore, in resharper if you create a pattern/search snippet you can then also automate you're workflow by converting simple prop fields into the above backing.
Search Pattern:
public $type$ $fname$ { get; set; }
Replace Pattern:
public $type$ $fname$
{
get { return Get<$type$>(); }
set { Set(value); }
}
I have written an article that helps with this (https://msdn.microsoft.com/magazine/mt736453). You can use the SolSoft.DataBinding NuGet package. Then you can write code like this:
public class TestViewModel : IRaisePropertyChanged
{
public TestViewModel()
{
this.m_nameProperty = new NotifyProperty<string>(this, nameof(Name), null);
}
private readonly NotifyProperty<string> m_nameProperty;
public string Name
{
get
{
return m_nameProperty.Value;
}
set
{
m_nameProperty.SetValue(value);
}
}
// Plus implement IRaisePropertyChanged (or extend BaseViewModel)
}
Benefits:
base class is optional
no reflection on every 'set value'
can have properties that depend on other properties, and they all automatically raise the appropriate events (article has an example of this)
I came up with this base class to implement the observable pattern, pretty much does what you need ("automatically" implementing the set and get). I spent line an hour on this as prototype, so it doesn't have many unit tests, but proves the concept. Note it uses the Dictionary<string, ObservablePropertyContext> to remove the need for private fields.
public class ObservableByTracking<T> : IObservable<T>
{
private readonly Dictionary<string, ObservablePropertyContext> _expando;
private bool _isDirty;
public ObservableByTracking()
{
_expando = new Dictionary<string, ObservablePropertyContext>();
var properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList();
foreach (var property in properties)
{
var valueContext = new ObservablePropertyContext(property.Name, property.PropertyType)
{
Value = GetDefault(property.PropertyType)
};
_expando[BuildKey(valueContext)] = valueContext;
}
}
protected void SetValue<T>(Expression<Func<T>> expression, T value)
{
var keyContext = GetKeyContext(expression);
var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);
if (!_expando.ContainsKey(key))
{
throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
}
var originalValue = (T)_expando[key].Value;
if (EqualityComparer<T>.Default.Equals(originalValue, value))
{
return;
}
_expando[key].Value = value;
_isDirty = true;
}
protected T GetValue<T>(Expression<Func<T>> expression)
{
var keyContext = GetKeyContext(expression);
var key = BuildKey(keyContext.PropertyName, keyContext.PropertyType);
if (!_expando.ContainsKey(key))
{
throw new Exception($"Object doesn't contain {keyContext.PropertyName} property.");
}
var value = _expando[key].Value;
return (T)value;
}
private KeyContext GetKeyContext<T>(Expression<Func<T>> expression)
{
var castedExpression = expression.Body as MemberExpression;
if (castedExpression == null)
{
throw new Exception($"Invalid expression.");
}
var parameterName = castedExpression.Member.Name;
var propertyInfo = castedExpression.Member as PropertyInfo;
if (propertyInfo == null)
{
throw new Exception($"Invalid expression.");
}
return new KeyContext {PropertyType = propertyInfo.PropertyType, PropertyName = parameterName};
}
private static string BuildKey(ObservablePropertyContext observablePropertyContext)
{
return $"{observablePropertyContext.Type.Name}.{observablePropertyContext.Name}";
}
private static string BuildKey(string parameterName, Type type)
{
return $"{type.Name}.{parameterName}";
}
private static object GetDefault(Type type)
{
if (type.IsValueType)
{
return Activator.CreateInstance(type);
}
return null;
}
public bool IsDirty()
{
return _isDirty;
}
public void SetPristine()
{
_isDirty = false;
}
private class KeyContext
{
public string PropertyName { get; set; }
public Type PropertyType { get; set; }
}
}
public interface IObservable<T>
{
bool IsDirty();
void SetPristine();
}
Here's the usage
public class ObservableByTrackingTestClass : ObservableByTracking<ObservableByTrackingTestClass>
{
public ObservableByTrackingTestClass()
{
StringList = new List<string>();
StringIList = new List<string>();
NestedCollection = new List<ObservableByTrackingTestClass>();
}
public IEnumerable<string> StringList
{
get { return GetValue(() => StringList); }
set { SetValue(() => StringIList, value); }
}
public IList<string> StringIList
{
get { return GetValue(() => StringIList); }
set { SetValue(() => StringIList, value); }
}
public int IntProperty
{
get { return GetValue(() => IntProperty); }
set { SetValue(() => IntProperty, value); }
}
public ObservableByTrackingTestClass NestedChild
{
get { return GetValue(() => NestedChild); }
set { SetValue(() => NestedChild, value); }
}
public IList<ObservableByTrackingTestClass> NestedCollection
{
get { return GetValue(() => NestedCollection); }
set { SetValue(() => NestedCollection, value); }
}
public string StringProperty
{
get { return GetValue(() => StringProperty); }
set { SetValue(() => StringProperty, value); }
}
}
Other things you may want to consider when implementing these sorts of properties is the fact that the INotifyPropertyChang *ed *ing both use event argument classes.
If you have a large number of properties that are being set then the number of event argument class instances can be huge, you should consider caching them as they are one of the areas that a string explosion can occur.
Take a look at this implementation and explanation of why it was conceived.
Josh Smiths Blog
An idea using reflection:
class ViewModelBase : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
bool Notify<T>(MethodBase mb, ref T oldValue, T newValue) {
// Get Name of Property
string name = mb.Name.Substring(4);
// Detect Change
bool changed = EqualityComparer<T>.Default.Equals(oldValue, newValue);
// Return if no change
if (!changed) return false;
// Update value
oldValue = newValue;
// Raise Event
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(name));
}//if
// Notify caller of change
return true;
}//method
string name;
public string Name {
get { return name; }
set {
Notify(MethodInfo.GetCurrentMethod(), ref this.name, value);
}
}//method
}//class
Use this
using System;
using System.ComponentModel;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
public static class ObservableFactory
{
public static T Create<T>(T target)
{
if (!typeof(T).IsInterface)
throw new ArgumentException("Target should be an interface", "target");
var proxy = new Observable<T>(target);
return (T)proxy.GetTransparentProxy();
}
}
internal class Observable<T> : RealProxy, INotifyPropertyChanged, INotifyPropertyChanging
{
private readonly T target;
internal Observable(T target)
: base(ImplementINotify(typeof(T)))
{
this.target = target;
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
if (methodCall != null)
{
return HandleMethodCall(methodCall);
}
return null;
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
IMessage HandleMethodCall(IMethodCallMessage methodCall)
{
var isPropertySetterCall = methodCall.MethodName.StartsWith("set_");
var propertyName = isPropertySetterCall ? methodCall.MethodName.Substring(4) : null;
if (isPropertySetterCall)
{
OnPropertyChanging(propertyName);
}
try
{
object methodCalltarget = target;
if (methodCall.MethodName == "add_PropertyChanged" || methodCall.MethodName == "remove_PropertyChanged"||
methodCall.MethodName == "add_PropertyChanging" || methodCall.MethodName == "remove_PropertyChanging")
{
methodCalltarget = this;
}
var result = methodCall.MethodBase.Invoke(methodCalltarget, methodCall.InArgs);
if (isPropertySetterCall)
{
OnPropertyChanged(methodCall.MethodName.Substring(4));
}
return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
}
catch (TargetInvocationException invocationException)
{
var exception = invocationException.InnerException;
return new ReturnMessage(exception, methodCall);
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanging(string propertyName)
{
var handler = PropertyChanging;
if (handler != null) handler(this, new PropertyChangingEventArgs(propertyName));
}
public static Type ImplementINotify(Type objectType)
{
var tempAssemblyName = new AssemblyName(Guid.NewGuid().ToString());
var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
tempAssemblyName, AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = dynamicAssembly.DefineDynamicModule(
tempAssemblyName.Name,
tempAssemblyName + ".dll");
var typeBuilder = moduleBuilder.DefineType(
objectType.FullName, TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
typeBuilder.AddInterfaceImplementation(objectType);
typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanging));
var newType = typeBuilder.CreateType();
return newType;
}
}
}
I use the following extension method (using C# 6.0) to make the INPC implemenation as easy as possible:
public static bool ChangeProperty<T>(this PropertyChangedEventHandler propertyChanged, ref T field, T value, object sender,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
if (comparer == null)
comparer = EqualityComparer<T>.Default;
if (comparer.Equals(field, value))
{
return false;
}
else
{
field = value;
propertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
return true;
}
}
The INPC implementation boils down to (you can either implement this every time or create a base class):
public class INPCBaseClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool changeProperty<T>(ref T field, T value,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
return PropertyChanged.ChangeProperty(ref field, value, this, comparer, propertyName);
}
}
Then write your properties like this:
private string testProperty;
public string TestProperty
{
get { return testProperty; }
set { changeProperty(ref testProperty, value); }
}
NOTE: You can omit the [CallerMemberName] declaration in the extension method, if you want, but I wanted to keep it flexible.
If you have properties without a backing field you can overload changeProperty:
protected bool changeProperty<T>(T property, Action<T> set, T value,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
bool ret = changeProperty(ref property, value, comparer, propertyName);
if (ret)
set(property);
return ret;
}
An example use would be:
public string MyTestProperty
{
get { return base.TestProperty; }
set { changeProperty(base.TestProperty, (x) => { base.TestProperty = x; }, value); }
}
I realize this question already has a gazillion answers, but none of them felt quite right for me. My issue is I don't want any performance hits and am willing to put up with a little verbosity for that reason alone. I also don't care too much for auto properties either, which led me to the following solution:
public abstract class AbstractObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetValue<TKind>(ref TKind Source, TKind NewValue, params string[] Notify)
{
//Set value if the new value is different from the old
if (!Source.Equals(NewValue))
{
Source = NewValue;
//Notify all applicable properties
foreach (var i in Notify)
OnPropertyChanged(i);
return true;
}
return false;
}
public AbstractObject()
{
}
}
In other words, the above solution is convenient if you don't mind doing this:
public class SomeObject : AbstractObject
{
public string AnotherProperty
{
get
{
return someProperty ? "Car" : "Plane";
}
}
bool someProperty = false;
public bool SomeProperty
{
get
{
return someProperty;
}
set
{
SetValue(ref someProperty, value, "SomeProperty", "AnotherProperty");
}
}
public SomeObject() : base()
{
}
}
Pros
No reflection
Only notifies if old value != new value
Notify multiple properties at once
Cons
No auto properties (you can add support for both, though!)
Some verbosity
Boxing (small performance hit?)
Alas, it is still better than doing this,
set
{
if (!someProperty.Equals(value))
{
someProperty = value;
OnPropertyChanged("SomeProperty");
OnPropertyChanged("AnotherProperty");
}
}
For every single property, which becomes a nightmare with the additional verbosity ;-(
Note, I do not claim this solution is better performance-wise compared to the others, just that it is a viable solution for those who don't like the other solutions presented.
I suggest to use ReactiveProperty.
This is the shortest method except Fody.
public class Data : INotifyPropertyChanged
{
// boiler-plate
...
// props
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
}
instead
public class Data
{
// Don't need boiler-plate and INotifyPropertyChanged
// props
public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>();
}
(DOCS)

Categories

Resources