ViewModel with IDataErrorInfo always calls getter after setting property? - c#

I want to give this question as much context as possible, but, in summary, I'm basically asking two questions:
Does WPF always call the getter after setting a bound property when the setter doesn't throw an exception?
Is it possible to prevent the getter of a bound property from being called after an error has occurred in the setter when the ViewModel implements IDataErrorInfo?
I currently have a Model class that implements validation by throwing an exception from the property setter. Additionally, many of the properties are coupled, so modifying the value of one of them may cause several others to be recalculated. It implements INotifyPropertyChanged to alert outside listeners whenever a recalculation has occurred. It looks something like this:
public class Model : INotifyPropertyChanged
{
private double property1;
public double Property1
{
get { return property1; }
set
{
if (value < 0.0)
throw new Exception("Property1 cannot be less than 0.0.");
property1 = value;
OnPropertyChanged(new PropertyChangedEventArgs("Property1"));
}
}
// ...Everything needed to support INotifyPropertyChanged...
}
Initially, I implemented the ViewModel for this class to act as a thin wrapper around the model properties, providing additional view-specific behaviors whenever an error occurs (flagging invalid data, disabling buttons, etc.):
public class ViewModel : INotifyPropertyChanged
{
private readonly Model model;
public ViewModel()
{
model = new Model();
model.PropertyChanged += (sender, args) => OnPropertyChanged(args);
}
public string Property1
{
get { return model.Property1.ToString(); }
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch (Exception)
{
// Perform any view-specific actions
throw;
}
}
}
// ...Everything needed to support INotifyPropertyChanged
}
Notably, the ViewModel doesn't have any additional backing fields; all of its properties are directly linked to the corresponding properties in the Model, and any PropertyChanged notifications from the Model are passed along by the ViewModel.
But, I've frequently heard that using exceptions for validation can be limiting, and I'm starting to realize the same, specifically as the need for cross-coupled validation rules has increased in this application.
I didn't want to change behaviors of the Model, since it is already being used in several other places, but I went about changing the ViewModel to implement IDataErrorInfo:
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private readonly Model model;
public ViewModel()
{
model = new Model();
model.PropertyChanged += (sender, args) =>
{
errorList.Remove(args.PropertyName);
OnPropertyChanged(args);
};
}
public string Property1
{
get { return model.Property1.ToString(); }
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch(Exception ex)
{
// Perform any view-specific actions
errorList["Property1"] = ex.Message;
}
}
}
private readonly Dictionary<string, string> errorList = new Dictionary<string, string>();
public string this[string propertyName]
{
get
{
string errorMessage;
if (errorList.TryGetValue(propertyName, out errorMessage))
return errorMessage;
return String.Empty;
}
}
public string Error { get { return String.Empty; } }
// ...Everything needed to support INotifyPropertyChanged
}
However, this caused a drastic unwanted change in behavior. When using exceptions, after the user entered an invalid value, the Validation.ErrorTemplate for the control is displayed and the invalid value remains in the control, thus giving the user the opportunity to correct their mistake. When using IDataErrorInfo, WPF seems to call the property getter after the setter has completed. Since the Model hasn't changed whenever an error occurs, the invalid value is replaced by the previous value. So now I have a control displaying the Validation.ErrorTemplate but with a VALID being value displayed!
It seems crazy to me that WPF would automatically call a property getter without receiving a PropertyChanged notification (after the window has been initialized). And it doesn't attempt to call the getter after an exception is thrown, so why would it do it when IDataErrorInfo is used?
Am I doing something wrong that's causing this behavior? Is there a way to prevent WPF from calling the property getter in the ViewModel after an error has occurred in the setter when the ViewModel implements IDataErrorInfo?
I've tried adding backing fields to the ViewModel to store the invalid value, but due to the fact Model properties can be modified outside of the ViewModel (that's the reason it implements INotifyPropertyChanged in the first place), the solution ends up being quite complex and basically unsustainable in an environment with programmers of varying skill levels.

Here's the approach you want to have for form validation in MVVM
Your model
public class Product:IDataErrorInfo
{
public string ProductName {get;set;}
public string this[string propertyName]
{
get
{
string validationResult = null;
switch (propertyName)
{
case "ProductName":
validationResult = ValidateName();
}
}
}
}
Then in your ViewModel
public string ProductName
{
get { return currentProduct.ProductName; }
set
{
if (currentProduct.ProductName != value)
{
currentProduct.ProductName = value;
base.OnPropertyChanged("ProductName");
}
}
}
As another consideration, when I want to validate numbers (such as your double validation), keep the model as having a double instead of a string
public double? Property1 {get;set;}
then you can do this
<Textbox Name="myDoubleTextbox" >
<Binding ValidatesOnDataErrors="true" Path="Property1" TargetValueNull="" />
/>
so when they type something incorrect into the double box, it sends null to your model and you can check against that.

Although it doesn't answer the questions I asked, my coworker suggested that the desired behavior could be achieved by adding the invalid value to the errorList and modifying the getter in the ViewModel to return the invalid value whenever there is an error.
So the properties in the ViewModel look like this:
public string Property1
{
get
{
ErrorInfo error;
if (errorList.TryGetValue("Property1", out error))
return error.Value;
return model.Property1.ToString();
}
set
{
try
{
model.Property1 = Double.Parse(value);
}
catch (Exception ex)
{
// Perform any view-specific actions
errorList["Property1"] = new ErrorInfo(value, ex.Message);
}
}
}
With the following updates to the IDataErrorInfo methods:
private struct ErrorInfo
{
public readonly string Value;
public readonly string Message;
public ErrorInfo(string value, string message)
{
Value = value;
Message = message;
}
}
private readonly Dictionary<string, ErrorInfo> errorList = new Dictionary<string, ErrorInfo>();
public string this[string propertyName]
{
get
{
ErrorInfo error;
if (errorList.TryGetValue(propertyName, out error))
return error.Message;
return String.Empty;
}
}
public string Error { get { return String.Empty; } }
This allows the PropertyChanged event handler to stay the same, and only requires small changes to the property getters and setters.

Related

ViewModel properties with multiple calls to PropertyChanged

Recently I've been learning C# and WPF for work. I'm trying to use MVVM on a project I'm working on, just to keep the code organized and learn how it works.
In MVVM, controls on the View bind to properties on the ViewModel, which implements INotifyPropertyChanged. Pretty often, when a certain property is updated, I'll want a bunch of other properties to get updated as a result.
For example, I have a ListBox with a TextBox above it. You can type in the TextBox, and it filters the stuff in the ListBox. But I also need to be able to clear the TextBox from code in certain cases. The code ends up looking like this:
private Collection<string> _listOfStuff;
public Collection<string> FilteredList
{
get
{
if (String.IsNullOrWhiteSpace(SearchText))
{
return _listOfStuff;
}
else
{
return new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
}
}
set
{
if (value != _listOfStuff)
{
_listOfStuff = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText"); // Tells the view to change the value of the TextBox
OnPropertyChanged("FilteredList"); // Tells the view to update the filtered list
}
}
}
As this project gets bigger, this is starting to feel sloppy. I have one setter with 6 calls to OnPropertyChanged and it's getting hard to keep track of stuff. Is there a better way to do this?
I tried out Assisticant on a project about a year ago. It figures out which of your properties need to raise notifications and also which are related. There is a good course for it on Pluralsight and the examples on the website are pretty good. If nothing else you could check out the source code to see how he did it.
Also some good suggestions from Change Notification in MVVM Hierarchies.
They mentioned:
Use an attribute -> e.g. [DependsUpon(nameof(Size))]
and
Josh Smith's PropertyObserver
Could put the raise property change calls in a method if you just need to raise the same notifications every time.
First you shouldn't do potentially expensive operations in a command, then you'll be able to remove the OnPropertyChanged("FilteredList"); from your SearchText.
So you should move that code from the getter and into it's own command and bind it from XAML (either as Command on a button or using Blends Interactivity Trigger to call it when the text fields value changes).
public ICommand SearchCommand { get; protected set; }
// Constructor
public MyViewModel()
{
// DelegateCommand.FromAsyncHandler is from Prism Framework, but you can use
// whatever your MVVM framework offers for async commands
SearchCommand = DelegateCommand.FromAsyncHandler(DoSearch);
}
public async Task DoSearch()
{
var result = await _listOfStuff.Where(x => x.Contains(SearchText)).ToListAsync();
FilteredList = new Collection<string>(result);
}
private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
get
{
return _filteredList;
}
set
{
if (value != _filteredList)
{
_filteredList = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get
{
return _searchText;
}
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText");
}
}
}
On a side note: You can also use OnPropertyChanged(nameof(FilteredList)); to have a refactor friendly version, when you rename your property all of your OnPropertyChanged calls will be updated to. Requires C# 6.0 though, but it's compatible with older .NET Frameworks (back to 2.0), but requires Visual Studio 2015 or later
For anyone searching for a good solution to this type of problem: Check out ReactiveUI.
It is a framework based on Reactive Extensions (Rx), with the idea that you model this type of dependencies between properties explicitly, without a jungle of RaisePropertyChanged(..).
Specifically check out the ObservableAsPropertyHelper (sometimes called OAPH).
You should only raise OnPropertyChanged in the setter of the property itself.
A cleaner implementation of your ViewModel can be:
private Collection<string> _listOfStuff;
private Collection<string> _filteredList;
public Collection<string> FilteredList
{
get
{
return _filteredList;
}
set
{
if (value != _filteredList)
{
_filteredList = value;
OnPropertyChanged("FilteredList");
}
}
}
private string _searchText;
public string SearchText
{
get { return _searchText; }
set
{
if (value != _searchText)
{
_searchText = value;
OnPropertyChanged("SearchText");
FilteredList = new Collection<string>(_listOfStuff.Where(x => x.Contains(SearchText)));
}
}
}
if you just don't wanna type only other option is to fire OnPropertyChanged for all properties which can be done by passing a null or string.Empty, although it will be sloppier code!
OnPropertyChanged(Null);
or
OnPropertyChanged(String.Empty);

How should the ViewModel refer to its Models properties?

As the ViewModel has the job to "prepare" the Model's properties to get displayed in the View, what is the best way of referring to the underlying Models properties from the ViewModel?
I could think about two solutions by now:
Option 1 - Duplicate the Model's properties in the ViewModel (wrapper-approach)
Architecture
class Model
{
public string p1 { get; set; }
public int p2 { get; set; }
}
class ViewModel : INotifyPropertyChanged
{
// Model-instance for this ViewModel
private Model M;
public string p1
{
get { return M.p1; }
set
{
M.p1 = value;
// assuming View controls are bound to the ViewModel's properties
RaisePropertyChanged("p1");
}
}
// let's say, I only want to check a Checkbox in the View,
// if the value of p2 exceeds 10.
// Raising the property changed notification would get handled
// in the modifying code instead of the missing setter of this property.
public bool p2
{
get
{
if (M.p2 > 10)
{ return true; }
else
{ return false; }
}
}
// Initialize the Model of the ViewModel instance in its c'tor
public ViewModel()
{ M = new Model(); }
}
Binding
<Textbox Text="{Binding p1}"/>
<Checkbox IsEnabled="False" IsChecked="{Binding p2, Mode=OneWay}"/>
Advantages
Full control about how the Model's properties are displayed on the View as shown in p2: int gets converted to bool on demand.
Changes of the properties of the ViewModel could be raised individual, might be a little performance increase compared to option 2.
Disadvantages
Violation of DRY.
More Code to write/maintain.
Modifications to the Model/ViewModel could easily become shotgun surgery.
Option 2 - Treat the whole Model as property of the ViewModel
Architecture
class Model
{
public string p1 { get; set; }
public int p2 { get; set; }
}
class ViewModel : INotifyPropertyChanged
{
// Model instance for this ViewModel (private field with public property)
private Model _M;
public Model M
{
get { return _M; }
set
{
_M = value;
// Raising the changing notification for the WHOLE Model-instance.
// This should cause ALL bound View-controls to update their values,
// even if only a single property actually got changed
RaisePropertyChanged("M");
}
}
// Initialize the Model of the ViewModel instance in its ctor
public ViewModel()
{ M = new Model(); }
}
Binding
<Textbox Text="{Binding M.p1}"/>
<Checkbox IsEnabled="False" IsChecked="{Binding M.p2, Mode=OneWay, Converter={StaticResource InverseBooleanConverter}"/>
Advantages
Can save a lot of code.
Reduces complexity.
Increases maintainability.
Disadvantages
In this approach, the ViewModel is nothing more than a continuous-flow water heater for the Models properties, except for some possible interaction logic for the View.
No control about how the Model's properties are displayed in the View - which ultimately leads to total needlessness of the ViewModel and implementation of conversion logic in the View.
It is the responsibility of your ViewModel to expose the Model to the View, you should not expose the Model's properties as additional properties in the ViewModel, instead, your View should bind directly to the model.
Additionally, it isn't wrong to have logic in your Model, in fact, it makes more sense to contain model related code within the model, as opposed to the ViewModel.
Here is an example:
public class Movie
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
//Notify property changed stuff (if required)
//This is obviously a stupid example, but the idea
//is to contain model related logic inside the model.
//It makes more sense inside the model.
MyFavourite = value == "My Movie";
}
}
private bool _MyFavourite;
public bool MyFavourite
{
get { return _MyFavourite; }
set
{
_MyFavourite = value;
//Notify property changed stuff.
}
}
}
So to answer your question a little more directly, you should expose your model in the view model as a property.
public class ViewModel
{
private Movie _Model;
public Movie Model
{
get { return _Model; }
set
{
_Model = value;
//Property changed stuff (if required)
}
}
...
}
Therefore, your View will bind to the Model property, like you have already done so.
EDIT
In the example for casting to the type, you can implement a read-only property in your Model, like so:
public bool MyBool
{
get
{
return MyInt > 10; }
}
}
Now the magic here would be that you will need to call the INotifyPropertyChanged for this property whenever MyInt changes. So your other property would look something like this:
public int MyInt
{
get { ... }
set
{
_MyInt = value;
//Notify property changed for the read-only property too.
OnPropertyChanged();
OnPropertyChanged("MyBool");
}
}
In my view, Model should not have RaisePropertyChanged stuff. Some view models (e.g. Blazor) might not need it, others (e.g. WPF) might use other mechanisms like DependencyProperty. Thus, to me Model is a POCO class. Hence, it becomes ViewModel responsibility to report changes to the data up to the View. Consequently, ViewModel is bound to wrap Model's properties (OA's option 1).
You might want to look at AutoMapper to centralize the mappings.

How to use Exception Messages for IDataErrorInfo on WPF

i was wondering how to best validate some user inputs on the WPF mvvm pattern? I've implemented IDataErrorInfo in my ViewModel. But I don't know how to use this interface with raising an exception. My purpose is to not have any logic in my viewModel. So the validation has to be in the business logic class.
The bounded field is the Property "Name" in my ViewModel. It is
ValidatesOnDataErrors=True
The Property in my viewmodel looks like this:
//Property in ViewModel
public string Name
{
get
{
return myBl.Name;
}
set
{
try
{
myBl.Name = value;
}
catch(InvalidCastException e)
{
Console.WriteLine(String.Format("Es ist eine Exception aufgetreten: {0}", e.Message));
}
}
}
The businesslogic lookes like that:
//BusinessLogic Class
public string Name
{
get { return name; }
set
{
if (value.Contains('1'))
throw new InvalidCastException("Cannot contain 1");
name = value;
}
}
The Exception is thrown as proposed but how to move on? I want the e.Message to be the ValidationErrorMessage.
The only examples i found were with validation in
public string this[string propertyName]
{
get
{ throw new NotImplementedException()}
}
But it not seems to be the doable way with exceptions.
Don't catch any exceptions in the ViewModel properties:
public string Name
{
get
{
return myBl.Name;
}
set
{
myBl.Name = value;
}
}
and check the ValidateOnExceptions option in the data binding dialog (or ValidatesOnExceptions=True in the XAML code).

How to validate view controls against data in the viewmodel

I'm new at WPF, and I'm getting my head around validators, it seems that you need to inherit from ValidationRule and override the Validate function, this totally separated from the view model, but what if I want to validate against some list/collection/set/dictionary in the viewmodel, to check whether this new input is not already in the list, a good example would be creating a validation to see whether or not an username is not already taken.
There are several different ways to do Validation in WPF. There's two main ways I can think of off the top of my head
Create Validation Rules then apply them in XAML
Implement IDataErrorInfo in your ViewModel
Validation Rules are specified in your XAML (GUI), while implementing IDataErrorInfo moves the Validation logic into your ViewModel (Business logic). While, ValidationRules are nice because you can create your own and reuse them, they also fail to provide validation in your business logic which is most likely required.
The concept of Client vs. Server side validation is interesting, perhaps as it pertains to Silverlight, but since you tagged this as WPF, I'm assuming the only difference is whether the validation occurs in the Views or ViewModels (UI or Business logic). It seem to me that even if your UI validated inputs, your ViewModels would still need to do proper validation.
Therefore, I suggest implementing IDataErrorInfo. By the way, the reason IDataErrorInfo works, is because a ValidationRule exists that checks for the IDataErrorInfo Interface! Here's an example of how I would do it in my ViewModelBase class:
Note: the immediately following examples ignore the fact you will likely need INotifyPropertyChanged notifications to update your bindings and instead focuses simply on Validation.
public class ViewModelBase : IDataErrorInfo
{
private Dictionary<string, string> errors = new Dictionary<string, string>();
// required for IDataErrorInfo
public virtual string Error
{
get { return String.Join(Environment.NewLine, errors); }
}
// required for IDataErrorInfo
public string this[string propertyName]
{
get
{
string result;
errors.TryGetValue(propertyName, out result);
return result;
}
}
// Useful property to check if you have errors
public bool HasErrors
{
get
{
return errors.Count > 0;
}
}
protected void SetError<T>(string propertyName, String error)
{
if (error == null)
errors.Remove(propertyName);
else
errors[propertyName] = error;
OnHasErrorsChanged();
}
protected string GetError<T>(string propertyName, String error)
{
String s;
errors.TryGetValue(propertyName, out s);
return s;
}
protected virtual void OnHasErrorsChanged()
{
}
}
Then your ViewModels can implement it like this:
public class MyViewModel : ViewModelBase
{
private string someProperty;
public string SomeProperty
{
get
{
return someProperty;
}
set
{
if(someProperty != null)
{
someProperty = value;
SetError("SomeProperty", ValidateSomeProperty());
}
}
}
private string ValidateSomeProperty()
{
if(String.IsNullOrEmpty(SomeProperty))
return "Value is required";
return null;
}
}
In your UI, you'll need to add ValidatesOnDataErrors and NotifyOnValidationError like this:
Text="{Binding SomeProperty, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, NotifyOnValidationError=True}"
Note: Passing in strings to represent properties is kinda ugly (it's not refactor safe if you rename the property but forget to rename the string). INotifyPropertyChanged is the same way when you want to notify of property changes for DataBindings. Prism's NotificationObject has a refactor safe solution to this and it looks like this instead:
Replace GetError / SetError in the previous example with this:
protected void SetError<T>(Expression<Func<T>> prop, String error)
{
String propertyName = PropertySupport.ExtractPropertyName(prop);
if (error == null)
errors.Remove(propertyName);
else
errors[propertyName] = error;
OnHasErrorsChanged();
}
protected string GetError<T>(Expression<Func<T>> prop, String error)
{
String propertyName = PropertySupport.ExtractPropertyName(prop);
String s;
errors.TryGetValue(propertyName, out s);
return s;
}
And then my ViewModelBase is something like this:
public class ViewModelBase : NotificationObject, IDataErrorInfo
Then to implement properties:
public class MyViewModel : ViewModelBase
{
private string someProperty;
public string SomeProperty
{
get
{
return someProperty;
}
set
{
if(someProperty != null)
{
someProperty = value;
SetError( () => SomeProperty, ValidateSomeProperty()); // update validation for property
RaisePropertyChanged( () => SomeProperty); // notify data bindings
}
}
}
I didn't show the implementation for RaisePropertyChanged but it is in Prism's NotificationObject which is open source and a free download. You can instead implement INotifyPropertyChanged yourself and either raise the event with strings (not refactor safe) or implement it yourself similar to the above implemention for SetError (extract the property name and fire the event with it).
You'll need this helper method:
public static class PropertySupport
{
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpresssion)
{
if (propertyExpresssion == null)
{
throw new ArgumentNullException("propertyExpression");
}
var memberExpression = propertyExpresssion.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException("The expression is not a member access expression.", "propertyExpression");
}
var property = memberExpression.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException("The member access expression does not access a property.", "propertyExpression");
}
var getMethod = property.GetGetMethod(true);
if (getMethod.IsStatic)
{
throw new ArgumentException("The referenced property is a static property.", "propertyExpression");
}
return memberExpression.Member.Name;
}
}
Edit: Simplified Refactor Safe Alternative
If you are using .NET 4.5 or later, you can use the CallerMemberAttribute like this example shows for INotifyPropertyChanged and my SetError implementation. Then you wouldn't need to extract the property name yourself via reflection as above (It simplifies it quite a bit).
Sorry to get off track talking about Property Change Notifications, but they go hand in hand if you want your DataBindings and Validation to work!
I Finally did it, this is how :
Xaml:
<Window.Resources>
<l:DataResource x:Key="ValidateFieldMethod" BindingTarget="{Binding IsFieldValid}"/>
</Window.Resources>
<xctk:IntegerUpDown Width="50" Maximum="300" Minimum="0">
<xctk:IntegerUpDown.Value>
<Binding Path="SelectedItem.TargetPosition" Mode="TwoWay">
<Binding.ValidationRules>
<l:CustomValidationRule Validator="{l:DataResourceBinding DataResource={StaticResource ValidateFieldMethod}}" />
</Binding.ValidationRules>
</Binding>
</xctk:IntegerUpDown.Value>
</xctk:IntegerUpDown>
The ValidatorRule:
public delegate bool CheckAgainstDataDelegate(object newValue, string fieldName);
public class CustomValidationRule : ValidationRule
{
public CheckAgainstDataDelegate Validator { get; set; }
public override ValidationResult Validate(object value, CultureInfo cultureInfo, BindingGroup owner)
{
return Validate((object)owner, cultureInfo);
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo, BindingExpressionBase owner)
{
switch (ValidationStep)
{
case ValidationStep.UpdatedValue:
case ValidationStep.CommittedValue:
value = (object)owner;
break;
}
return new ValidationResult(Validator(value, ((BindingExpression) owner).ResolvedSourcePropertyName), null);
}
[Obsolete("Use Validator property of type delegate instead to validate the data",true)]
public override ValidationResult Validate(object value, CultureInfo cultureInfo) { return null; } //not used anymore
}
In the viewModel :
private CheckAgainstDataDelegate _isFieldValid;
public CheckAgainstDataDelegate IsFieldValid
{
get
{
return _isFieldValid
?? (_isFieldValid = delegate (object newValue,string propertyName)
{
switch (propertyName)
{
case "TargetPosition":
var newV = (int) newValue;
return Items.All(e => e.TargetPosition != newV);
default:
throw new Exception("Property Assigned to unknown field");
}
});
}
}
I used the help of http://www.wpfmentor.com/2009/01/how-to-add-binding-to-property-on.html to Bind in the ValidationrRule.
What do you Think?

Container for properties values

When .NET 4.5 was released i started using such great Attribute as CallerMemberName. It's easier to understand code, developers can write it faster also. It's like a snippet, not only a feature for debug/test purposes.
So I have a question. Is it normal to create and use something like this?
public class PropertyStore
{
Dictionary<string, object> data = new Dictionary<string,object>();
ViewModelBase modelBase;
internal PropertyStore(ViewModelBase _base)
{
modelBase = _base;
}
public void SetValue<T>(T value = default(T), [CallerMemberName] string prop = "")
{
T prev = GetValue<T>(prop);
if ((prev == null && value == null) || (prev != null && prev.Equals(value))) return;
data[prop] = value;
modelBase.OnPropertyChanged(prop);
}
public T GetValue<T>([CallerMemberName] string prop = "")
{
if (!data.ContainsKey(prop))
data[prop] = default(T);
return (T)data[prop];
}
}
Class-helper, that makes other class more readable, and also we have list of our properties without need to use Reflection.
The usage is:
public class SampleClass : ViewModelBase
{
PropertyStore PropertyStore;
public SampleClass ()
{
PropertyStore = new PropertyStore(this);
}
public string Key
{
get { return PropertyStore.GetValue<string>(); }
set { PropertyStore.SetValue(value); }
}
public DateTime Date
{
get { return PropertyStore.GetValue<DateTime>(); }
set { PropertyStore.SetValue(value); }
}
public bool IsSelected
{
get { return PropertyStore.GetValue<bool>(); }
set { PropertyStore.SetValue(value); }
}
}
The class ViewModelBase here simply implements INotifyPropertyChanged interface.
As I understand, this approach is something like Microsoft Dependency Properties, but I don't need all power of DependencyObject class, and I don't want inherit it.
With something like this I can use Binding, because it's enough to implement INotifyPropertyChanged, also we have no fields (as for me, i try to use properties smarter, than using fields directly (however, there is no problem to use Dictionary directly ^_^))
Sorry for my bad English... Not main language and not much practice.
Another Sample (after moving Methods to base class)
public class SampleClass : ViewModelBase
{
public string Key
{
get { return GetValue<string>(); }
set { SetValue(value); }
}
public DateTime Date
{
get { return GetValue<DateTime>(); }
set { SetValue(value); }
}
public bool IsSelected
{
get { return GetValue<bool>(); }
set { SetValue(value); }
}
}
No diff with Microsoft's WPF Property System.
Only feature you'll get with it is an ability to access property values via Dictionary.Get|Set methods.
You can get this ability with field based implementation of INotifyPropertyChanged. You can access property values by its name using dictionary, with property name to precompiled delegate mapping like it done in Yappi project.
var dateValue= Property<SampleClass>.Get<DateTime>(this,"Date");
Property<SampleClass>.Set<DateTime>(this,"Date",DateTime.Now);
Both can be rewritten as extension methods.
Nice idea, property bag without reflection and it will even work with obfuscation.
I don't see major problems with it but you may consider the following:
The prop parameter is optional so potentially a bug can be introduced by given a value in the call.
Value types will get boxed.
Access to the fields is relatively more expensive, can be a factor more expensive as you have much more code in a simple get (especially with boxing).
Dictionary takes more space than the number of properties you keep in (especially with boxing).
Each property also stores a string of the property name adding to the overhead.

Categories