Reputation: 27575
I'm trying to create code for the use-case in the code below, where a generic ViewModel class "captures" all the properties from its model, and presents properties with the same name and type, and also triggers PropertyChanged
event for data-binding.
Is there a way to do it? I'm using .NET 4.6.
public class Rectangle
{
public double Width {get; set;}
public double Height {get; set;}
}
public class RectangleViewModel : MagicViewModel<Rectangle>
{
public RectangleViewModel(Rectangle model)
: base(model){ }
}
public class MagicViewModel<TModel> : INotifyPropertyChanged
{
protected readonly TModel _model;
public MagicViewModel(TModel model)
{
_model = model;
}
// inpc implementation
// what else?
}
public class Program
{
public static void Main(string[] args)
{
var vm = new RectangleViewModel(new Rectangle());
var calls = 0;
vm.PropertyChanged += (sender, args) => calls++;
vm.Height = 10; // magic happened here
Debug.Assert(calls > 0);
}
}
Upvotes: 0
Views: 210
Reputation: 169190
You could use Fody to automatically inject code that raises the PropertyChanged
event for all properties of the model classes at compile time.
Then you could bind directly to Rectangle
without modify it and explicitly implement the INotifyPropertyChanged
interface.
Othwerwise I am afraid that you will have to define each property, one-by-one, in each view model or come up with a way to auto-generate the view model classes before you build.
Upvotes: 2
Reputation: 592
Is something like this what you're looking for?
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.CompilerServices;
public class Rectangle : INotifyPropertyChanged
{
private double height;
public double Width { get; set; }
public double Height { get => height; set => SetField(ref height, value); }
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
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;
}
#endregion
}
public class RectangleViewModel : MagicViewModel<Rectangle>
{
public RectangleViewModel(Rectangle model)
: base(model)
{
this.model = model;
model.PropertyChanged += (s, e) => OnPropertyChanged(e.PropertyName);
}
private Rectangle model;
public Rectangle Model { get => model; set => SetField(ref model, value); }
}
public class MagicViewModel<TModel> : INotifyPropertyChanged
{
protected readonly TModel _model;
public MagicViewModel(TModel model)
{
_model = model;
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public 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;
}
#endregion
}
public class Program
{
public static void Main(string[] args)
{
var vm = new RectangleViewModel(new Rectangle());
var calls = 0;
vm.PropertyChanged += (sender, propChangedArgs) => calls++;
vm.Model.Height = 10; // magic happened here
Debug.Assert(calls > 0);
}
}
Upvotes: 0