I'm trying to find an elegant solution to implement INotifiyDataErrorInfo with Caliburn.Micro MVVM framework.
I want to limit the amount of code that will be repeated in each VM that needs to implement the validation.
I started by writing a class that inherits Screen and implements INotifiyDataErrorInfo. It works correctly and all is fine, until I need validation on a VM that is not a Screen, but a Conductor.
Of course, I could make a class that inherits Conductor and implements INotifyDataErrorInfo but that's quite annoying as I would have to basically make my own version of all "base" classes of Caliburn.Micro.
One solution I had was to keep the Screen base class and create a IValidator interface that I would inject into my VM, something like this:
public interface IValidator<T> where T : INotifyDataErrorInfo
{
void Validates(T instance);
IEnumerable GetErrors(string propertyName);
bool HasErrors { get; }
void Validate();
void ValidateProperty<TValue>(TValue value, string propertyName = null);
void ValidateProperty<TValue, TProperty>(TValue value, Expression<Func<TProperty>> property);
}
It will then used in the VM in this way.
public class CreateCarViewModel : Conductor<CreateCarViewModel>.Collection.OneActive, INotifyDataErrorInfo
{
private readonly IValidator<CreateCarViewModel> validator;
public CreateExperimentViewModel(IValidator<CreateCarViewModel> validator)
{
this.DisplayName = "Select a car";
this.validator = validator;
this.validator.Validates(this);
}
[Required]
public string CarName
{
get
{
return this.carName;
}
set
{
if (this.carName != value)
{
this.carName = value;
this.validator.ValidateProperty(value, () => this.CarName);
this.NotifyOfPropertyChange(() => CarName);
}
}
}
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public System.Collections.IEnumerable GetErrors(string propertyName)
{
return this.validator.GetErrors(propertyName);
}
public bool HasErrors
{
get { return this.validator.HasErrors; }
}
...
}
This works pretty nicely, as it is very simple to implement the INotifyDataErrorInfo in the VMs, but the issue I have is triggering the ErrorChanged event. It must be triggered by the implementation of the IValidator as he is the one who knows when the errors have changed, and of course he cannot trigger directly.
One idea I have was to have an event in the IValidator and subscribe to it in the VM so that it can trigger its own event, but I find that it makes a lot of code for nothing.
Does anyone have a better idea?
Thanks
I wrote a small plugin for CM to enable fluent builder-style validation. Maybe it will help you. Feel free to use it: https://github.com/AIexandr/Caliburn.Micro.Validation
Example of usage:
public class PaymentEditorViewModel() : ValidatingScreen
{
public PaymentEditorViewModel()
{
AddValidationRule(() => PaymentSum).Condition(() => PaymentSum <= 0).Message("Please enter payment sum");
}
#region PaymentSum property
decimal _PaymentSum;
public decimal PaymentSum
{
get
{
return _PaymentSum;
}
set
{
_PaymentSum = value;
NotifyOfPropertyChange(() => PaymentSum);
}
}
#endregion
}
The wireup code is not excessive is it, if you have the IValidator expose the same event as the VM, eg:
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
public void Validate()
{
if (ErrorsChanged != null)
ErrorsChanged(instance, new DataErrorsChangedEventArgs("someProperty"));
}
and in the VM:
validator.ErrorsChanged += (sender, args) => ErrorsChanged(sender, args);
But I guess you already answered your own question without telling us ;)
Related
If I wanted to create a Attribute (derived from System.Attribute) that hooks into the .NET Build process and translates/converts a standard C# auto property like:
[Notify]
public string Name { get; set; }
to this code, which then is compiled:
private string _nameField;
public string Name
{
get => _nameField;
set
{
if (!EqualityComparer<string>.Default.Equals(_nameField, value))
{
_nameField = value;
NotifyPropertyChanged(nameof(Name));
}
}
}
How would I achive it? What would I have to do?
How can I let the attribute hook into the Build?
As you can see I have no clues at all of the Build process, nor of Roslyn.
But I want to get rid of superfluous MVVM boilerplate code and no longer spent too much time for dull repetitive typing....
Thx, Chris
There's a Fody weaver for this, called PropertyChanged.
Fody is a system for modifying your code at the end of the compile process. The PropertyChanged weaver automatically impements change notification for all properties in all classes that implement INotifyPropertyChanged. It has a number of ways to control the generation using attributes, implementing methods, etc.
The example from their GitHub project page starts like this:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName => $"{GivenNames} {FamilyName}";
}
The weaver interprets the above and generates code like this:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get => givenNames;
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged(InternalEventArgsCache.GivenNames);
OnPropertyChanged(InternalEventArgsCache.FullName);
}
}
}
string familyName;
public string FamilyName
{
get => familyName;
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged(InternalEventArgsCache.FamilyName);
OnPropertyChanged(InternalEventArgsCache.FullName);
}
}
}
public string FullName => $"{GivenNames} {FamilyName}";
protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
{
PropertyChanged?.Invoke(this, eventArgs);
}
}
internal static class InternalEventArgsCache
{
internal static PropertyChangedEventArgs FamilyName = new PropertyChangedEventArgs("FamilyName");
internal static PropertyChangedEventArgs FullName = new PropertyChangedEventArgs("FullName");
internal static PropertyChangedEventArgs GivenNames = new PropertyChangedEventArgs("GivenNames");
}
Of course you won't have access to that version, since it happens somewhere around the end of the compilation pass and your source is unaffected. Debugging the code can be a little difficult.
To help with that the PropertyChanged weaver looks for pre-implemented methods matching various rules and generates code to call those. If you have an OnPropertyChanged method in the class it'll call that instead of generating a boilerplate version. Or you can add an OnFamilyNameChanged method to the class above and that will be called before the OnPropertyChanged method.
You can use Roslyn source generators for that: Source Generators Cookbook
There's an INotifyPropertyChanged example.
I am stuck with a pattern for a long time now and recently I realized I forgot why I got used to do things this way.
public event EventHandler<string> SomethingChanged;
private void OnSomethingChanged(string something) => SomethingChanged?.Invoke(this, something);
So, when I want to invoke the event I call OnSomethingChanged(this, something);. Why not just do SomethingChanged?.Invoke(this, state); directly?
Feels like quite a simplification. Is it possible this OnSomethingChanged pattern served a purpose back then, when C# syntax was more complicated, but today it's mostly unnecessary?
In fact, since this pattern is quite long, I always just copy paste it and replace the relevant parts. Ironically, I can recall a couple of times when this copy pasting resulted debugging my software just to find out I made a mistake here, because of being in a hurry. This also makes invoking directly SomethingChanged preferable.
consider the following example:
public class BaseClass
{
private int id;
public int Id
{
get { return id; }
set { id = value; SomethingChanged(this, "id"); }
}
public event EventHandler<string> SomethingChanged;
}
public class DerivedClass : BaseClass
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
//// cannot be invoked directly here
// SomethingChanged(this, "name");
}
}
}
c# documentations mentions this case specifically
Events are a special type of delegate that can only be invoked from within the class that declared them. Derived classes cannot directly invoke events that are declared within the base class. Although sometimes you may want an event that can only be raised by the base class, most of the time, you should enable the derived class to invoke base class events. To do this, you can create a protected invoking method in the base class that wraps the event. By calling or overriding this invoking method, derived classes can invoke the event indirectly.
private method in the base class which triggeres event isn't much help, it should be protected. It is also sensible to make that method virtual to allow derived class add some functionality before/after notifing event subscrivers:
public class BaseClass
{
private int id;
public int Id
{
get { return id; }
set { id = value; OnSomethingChanged( "id"); }
}
public event EventHandler<string> SomethingChanged;
protected virtual void OnSomethingChanged(string something)
{
SomethingChanged(this, something);
}
}
public class DerivedClass : BaseClass
{
private string name;
public string Name
{
get { return name; }
set
{
name = value;
OnSomethingChanged("name");
}
}
protected override void OnSomethingChanged(string something)
{
base.OnSomethingChanged(something + " (event triggered from derived class)");
}
}
public static void Main(string[] args)
{
var item = new DerivedClass();
item.SomethingChanged += (e,s) => Console.WriteLine(s);
item.Id = 5;
item.Name = "abc";
}
this modified example prints:
id (event triggered from derived class)
name (event triggered from derived class)
How can I use C# generics to avoid having to create an extension function like so for each and every auto-generated class (Linq to SQL DBML)?
static public CharacterViewModel ToViewModel(this Character c)
{
return new CharacterViewModel(c);
}
Having a function like this provides a fairly clean way of selecting a set of items from the DB as their corresponding ViewModel, like so:
var characters = new ObservableCollection<CharacterViewModel>(from p in DB.Characters
select p.ToViewModel());
I'd like to see something like:
static public T ToViewModel<T,K>(K dbmlClass)
{
return new T(dbmlClass);
}
But I have a feeling this will involve Reflection-style object generation and I don't know how efficient that would be (or how to accomplish it).
By the way, I did previously investigate operator overloading the assignment ('=') as a possible solution, which could provide implicit casting, but I believe this would require overloading the = in the auto-generated class which I am not able to do.
Update
Thanks all, for the answers. I think I have a few avenues to check out now. To provide a bit more context, as some mentioned it wasn't clear. All of my ViewModels are derived from the following:
public class BaseDO<T>: BaseDO
{
public BaseDO(T model)
{
Model = model;
}
public T Model { get; set; }
}
abstract public class BaseDO: INotifyPropertyChanged, INotifyDeleted
{
#region Standard INotifyPropertyChanged Implementation
public void NotifyPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged = (o, e) => { };
#endregion
public event EventHandler OnEntityDeleted = (o, e) => { };
public void NotifyEntityDeleted()
{
OnEntityDeleted(this, new EventArgs());
}
}
public interface INotifyDeleted
{
event EventHandler OnEntityDeleted;
void NotifyEntityDeleted();
}
You could use a library such as Automapper. With this you have 1 place where you setup the configuration of how objects are mapped. You can then have:
public static T ToViewModel<T,K>(K dbmlClass)
{
return AutoMapper.Mapper.Map<T>(dbmlClass);
}
// Register mappings
public static void ConfigureMappings()
{
AutoMapper.Mapper.CreateMap<Character, CharacterViewModel>();
}
It's not really clear from your question whether you want to generate the view model classes automatically or whether you've already written them and each one accepts the model as a constructor parameter. If the former then I personally add INPC to the model classes using either Castle Proxy or Frody. If the latter then I believe this is what you're after:
public static class Helper
{
static public T ToViewModel<T>(this object dbmlClass)
{
return (T)Activator.CreateInstance(typeof(T), dbmlClass);
}
}
Which you would then use like this:
var model = new Model();
var view_model = model.ToViewModel<ViewModel>();
I am creating the program-side architecture of a software developped in WPF, I designed the architecture as being compliant with the MVVM pattern.
For many sakes (design, coherence, reusability, maintainability, scalability, etc) I created the class BaseViewModel implementing the interface INotifyPropertyChanged and some other methods:
public class BaseViewModel: INotifyPropertyChanged
{
private PropertyChangedEventHandler property_changed;
public event PropertyChangedEventHandler PropertyChanged
{
add { property_changed += value; }
remove { property_changed -= value; }
}
//Here several methods using PropertyChanged and easing the usage of ViewModels
public BaseViewModel() { }
}
The above-defined class BaseViewModel is used as a base class for all the other ViewModels of the application (or, at least, is meant to be so), for example:
public class SampleViewModel : BaseViewModel
{
//private PropertyChangedEventHandler property_changed;
//public event PropertyChangedEventHandler PropertyChanged
//{
// add { property_changed += value; }
// remove { property_changed -= value; }
//}
public String Name
{
get { return name; }
set
{
if(value != name)
{
name = value;
var handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs("Name"));
}
}
}
}
private String name = "";
public SampleViewModel ()
: base() { }
}
I use the class SampleViewModel as the DataContext of SampleUserControl which bares a DependencyProperty:
public partial class SampleUserControl : UserControl
{
#region ViewModel
public SampleViewModel ViewModel
{
get { return view_model; }
}
private SampleViewModel view_model = new SampleViewModel();
#endregion
#region DependencyProperty
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(SampleUserControl),
new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(TextPropertyChangedCallback)));
private static void TextPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
SampleUserControl sender = d as SampleUserControl;
if (sender != null)
{
sender.ViewModel.Name = (String)e.NewValue;
}
}
#endregion
public SampleUserControl()
{
InitializeComponent();
LayoutRoot.DataContext = ViewModel;
ViewModel.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}
void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
SampleViewModel viewmodel = sender as SampleViewModel;
if (viewmodel != null)
{
switch (e.PropertyName)
{
case "Name":
SetValue(TextProperty, viewmodel.Name);
break;
default:
break;
}
}
}
}
To sum up, the data relative to SampleUserControl are contained at three locations : the instance of SampleViewModel, within TextProperty and within the property Text of a TextBox in the xaml part of SampleUserControl(this property Text is twoway-bound through Binding with the field Name of ViewModel).
To synchronize the three values, I added the methods TextPropertyChangedCallback and ViewModel_PropertyChanged which update the fields which need to be updated.
The above code works and the three above-mentionned locations are kept up-to-date, events fire and so on, things are fine when SampleUsercontrol is consumed with data-binding.
But SampleViewModel fires the event BaseViewModel.PropertyChanged, and since BaseViewModel is meant to be extensively used, I would like each ViewModel to have its own event PropertyChanged, at least in order to avoid overlapping events.
So I uncomment the code of SampleViewModel thus redefining the event PropertyChanged but it breaks down the synchronization between the field Name of the instance of SampleViewModel and the property TextProperty of SampleUserControl.
Am I making some mistakes on the conception side?
Do you have any guidance for me?
What is the best economic way of defining a different event PropertyChanged for each ViewModel inheriting from BaseViewModel while still using the general-purpose methods defined within that base class (such methods use PropertyChanged)?
(I would like to avoid having heavy pieces of code to copy-paste.)
I know that it is more about optimization, but such optimizations can make a difference between a slow software and a fast one. I am at the stage of code-factoring, so I fancy nicely-shaped, elegant and factorized code.
End of the day happening, I may miss some obvious solutions.
Thanks in advance for any clue,
Julien
TL;DR: Basically, I would double-check that you are doing your DC/DP on that user control correctly, and toss out any concept of multiple definitions of PropertyChanged
In detail:
You defined PropertyChanged in the base class, which is great. There is no reason to ever redefine it anywhere else. Really, you are just asking for trouble by doing this.
Related to that, you should really just make a method to do the event invocation rather than doing the whole handler bit in the settter. Insta-reduction of copy paste.
The fact that you are having to use TextPropertyChanged is a huge red flag here. Which relates to the real problem, that you are probably abusing your dependency property. DPs are used to allow parent controls to bind to a property of your user control. You typically won't use them in conjunction with a data context internal to the control because, as you have seen, keeping them in sync is a nightmare.
In general, user controls should only have their own data context if they are set up to stand apart from any other control (ie, a sub-view). If they are just a fancy control, then giving them a view model rarely gets you anything.
C# - .net 3.5
I have a family of classes that inherit from the same base class.
I want a method in the base class to be invoked any time a property in a derrived class is accessed (get or set). However, I don't want to write code in each and every property to call the base class... instead, I am hoping there is a declarative way to "sink" this activity into the base class.
Adding some spice to the requirement, I do need to determine the name of the property that was accessed, the property value and its type.
I imagine the solution would be a clever combination of a delegate, generics, and reflection. I can envision creating some type of array of delegate assignments at runtime, but iterating over the MemberInfo in the constructor would impact performance more than I'd like. Again, I'm hoping there is a more direct "declarative" way to do this.
Any ideas are most appreciated!
You can't do it automatically, but you can pretty much get 95% for free. This is a classic case for aspect-oriented programming. Check out PostSharp, which has the OnFieldAccessAspect class. Here's how you might solve your problem:
[Serializable]
public class FieldLogger : OnFieldAccessAspect {
public override void OnGetValue(FieldAccessEventArgs eventArgs) {
Console.WriteLine(eventArgs.InstanceTag);
Console.WriteLine("got value!");
base.OnGetValue(eventArgs);
}
public override void OnSetValue(FieldAccessEventArgs eventArgs) {
int i = (int?)eventArgs.InstanceTag ?? 0;
eventArgs.InstanceTag = i + 1;
Console.WriteLine("value set!");
base.OnSetValue(eventArgs);
}
public override InstanceTagRequest GetInstanceTagRequest() {
return new InstanceTagRequest("logger", new Guid("4f8a4963-82bf-4d32-8775-42cc3cd119bd"), false);
}
}
Now, anything that inherits from FieldLogger will get the same behavior. Presto!
I don't believe this is possible to do declaratively, i have never seen it done that way. What you can do though is implement the INotifyPropertyChanged interface on your base class, and have the implementation of the interface in the base class. Something like this:
public class A : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected virtual void RaiseOnPropertyChanged(object sender, string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(sender, new PropertyChangedEventArgs(propertyName);
}
public A()
{
this.PropertyChanged += new PropertyChangedEventHandler(A_PropertyChanged);
}
void A_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//centralised code here that deals with the changed property
}
}
public class B : A
{
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
RaiseOnPropertyChanged(this, "MyProperty");
}
}
public string _myProperty = null;
}