Most efficient way to notify view(WPF/C#) - c#

I have a Car class.
class Car
{
string ModelNum;
}
Then I have Car instance
class CarInstance
{
string RegistrationNum;
}
There are several instances of Cars in my viewmodel.
ViewModel(Car car)
{
CarInstance Ins = car.GetInstance();
}
Question: Say that the Car itself is changing and it need to notify the view models of that change. What is the most efficient way to do this. I am aware I can use events(including eventaggregator in PRISM). I would like to know if there is any faster way to do this.
An Action parameter which can call multiple subscribers? Any such ideas?
All pseudocode.

Is INotifyPropertyChanged more performing than events?
The INotifyPropertyChanged interface defines a PropertyChanged event, i.e. it actually uses an event to notify the view.
Say that the Car itself is changing and it need to notify the view models of that change. What is the most efficient way to do this. I am aware I can use events(including eventaggregator in PRISM). I would like to know if there is any faster way to do this.
Raising an event will be very minor in terms of performance in most cases so you shouldn't worry about this:
How much performance overhead is there in using events?
The benefit of using an event aggregator is that your classes become more loosly coupled to each other compared to keeping a direct reference to each subscriber from each publisher class: https://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/
But raising an event, either using strong references or an event aggregator, is a good, recommended and fast way of notifying the outside world of a change.

Its absolute normal to put the property changed notifications in your models if they are bound to your views. It is the preferred approach over anything in MVVM. This is how you can do it
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
public class Car: INotifyPropertyChanged
{
#region Property Changed
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private string _modelNum;
public string ModelNum
{
get{ return _modelNum; }
set
{
_modelNum = value;
//this will raise the notification
OnPropertyChanged("ModelNum");
}
}
}
If this car object is bound to your View, You can
Text="{Binding Car.ModelNum, UpdateSourceTrigger=PropertyChanged}"
UpdateSourceTrigger=PropertyChanged will update the UI whenever ModelName is updated or will update the ModelName whenever you update from UI.

Create the following class for notiyfing:
public abstract class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private readonly Dictionary<string, object> _properties = new Dictionary<string, object>();
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);
}
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;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public void Notify<T>(Expression<Func<T>> memberExpression)
{
if (memberExpression == null)
{
throw new ArgumentNullException("memberExpression");
}
var body = memberExpression.Body as MemberExpression;
if (body == null)
{
throw new ArgumentException("Lambda must return a property.");
}
var vmExpression = body.Expression as ConstantExpression;
if (vmExpression != null)
{
LambdaExpression lambda = Expression.Lambda(vmExpression);
Delegate vmFunc = lambda.Compile();
object sender = vmFunc.DynamicInvoke();
PropertyChanged?.Invoke(sender, new PropertyChangedEventArgs(body.Member.Name));
}
}
}
Your ViewModel should look something like this.
public class ViewModel : Notifier
{
public ObservableCollection<String> Messages
{
get { return Get<ObservableCollection<String>>(); }
set
{
Set(value);
Notify(() => AreThereMessages);
}
}
public bool AreThereMessages => Messages?.Count > 0;
}
You don't need any private variables with the Notifier class above.
Getter: get { return Get<T>(); }
Setter: get { Set(value); }
Notify other property: Notify(() => OtherProperty);
Example view:
<DataGrid ItemsSource="{Binding Messages}"/>
<Button IsEnabled="{Binding AreThereMessages}"/>
The code above notifies a button if the messages collection is empty or not.
This is the easiest way I know to handle your viewmodel without additional libraries.

Related

How to subscribe and pass along collection changed event of child observable collection?

I have a ViewModel wrapping an instance of a model object and acting as an interface between the model and the View. In my case, the model has a collection that I connect to the view with an observable collection in the view model.
For instance :
public class NotificationBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetField<T>(ref T field, T value, [CallerMemberName] String fieldName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
RaiseChangedEvent(fieldName);
return true;
}
protected T SetProperty<T>(T property, T value, [CallerMemberName] String propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(property, value)) RaiseChangedEvent(propertyName);
return value;
}
protected void RaiseChangedEvent(string changedObjectName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(changedObjectName));
}
}
public class ViewModel : NotificationBase
{
public ViewModel(ref Model model)
{
_model = model ?? throw new ArgumentNullException("Model cannot be null");
}
public ObservableCollection<Thing> ViewModelCollection
{
get { return _model.ModelCollection; }
set { _model.ModelCollection = SetProperty(_model.ModelCollection, value); }
}
private Model _model { get; set; }
}
My question is : how do I make the view know when _model.Collection and thus ViewModelCollection are changed ?
Note : Model.Collection actually is an ObservableCollection but I don't know how to pass the CollectionChanged event along to the ViewModel and then to the View.

How to find the current method called?

In my sample, I had used property changed event. in this handler, I had an declare a method. each and every time that method fire when changing the property,
in That method, I had set the value to the property. when I set the value, it is a call to the event handler. so it's executing the circular. how to make the method call only one time?
private string name;
public string Name
{
get { return name; }
set
{
name= value;
Name.PropertyChanged+=(s,e)=>
{
Changed(s as string);
};
}
}
private void changed(string name)
{
Name = name;
}
in this code, the changed property call every time.
The basic thing is nameof keyword:
changed(nameof(Name));
You can go futher and omit the need of specifying name at all by adding the following CallerMemberName attribute to your method's parameter:
private void changed([CallerMemberName]string name=null){}
In this case you can call this method without property name: changed();
I'd hazard a guess you want to implement MVVM. The most elegant way to implement it so far is to have the following base class:
public abstract class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetPropertyAndNotifyIfNeeded<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
NotifyPropertyChanged(propertyName);
return true;
}
protected void NotifyPropertyChanged([CallerMemberName]string name=null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Implementation of your MVVM:
class Class1:Observable
{
public Class1()
{
}
string propertyValue;
public string Property
{
get => propertyValue;
set => SetPropertyAndNotifyIfNeeded(ref propertyValue, value);
}
}
As per your code remove subscription from your property to avoid recursive loop:
Name.PropertyChanged+=(s,e)=>
In your property call changed(nameof(Name));
class a:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
....
private string name;
public string Name
{
get { return name; }
set
{
if (name!=value)
{
name= value;
changed();
}
}
}
private void changed([CallerMemberName]string name=null)
{
PropertyChanged.?Invoke(this, new PropertyChangedEventArgs(name));
}
...
}
As I can see from your question, and I would guess, you would like to raise a Property Changed on it and set a value. Basically implement MVVM.
A really simple and quick way, would be to use a already existing Nuget package, which would simplify your job.
One that you can use is GalaSoft.MvvmLight.
After you add the Nuget package, you can just use it in the following way:
public string name;
public string Name
{
get { return name; }
set
{
name= value;
RaisePropertyChanged(nameof(Name));
}
}
RaisePropertyChanged, is going to Raise the PropertyChanged event for you (as the name itself suggests).

How to make a model class observable in WPF

Scenario
I get a model class from an external component or a code part where I do not want to alter something.
I'd like to bind this class to some WPF UI.
I also would like to refresh the UI if this model gets altered.
Question
Do I really need to write a wrapper class all the time, that creates PropertyChanged events for each setter?
How could I prevent to write all this clue coding manually?
What started like this ...
public class User : IUser
{
public String Name { get; set; }
public bool CurrentlyLoggedIn { get; set; }
// ...
}
... will always be bloated like so
public class UserObservableWrapper : IUser, INotifyPropertyChanged
{
public String Name
{
get
{
return this.innerUser.Name;
}
set
{
if (value == this.innerUser.Name)
{
return;
}
this.innerUser.Name = value;
this.OnPropertyChanged( "Name" );
}
}
public bool CurrentlyLoggedIn
{
get
{
return innerUser.CurrentlyLoggedIn;
}
set
{
if (value.Equals( innerUser.CurrentlyLoggedIn ))
{
return;
}
innerUser.CurrentlyLoggedIn = value;
this.OnPropertyChanged( "CurrentlyLoggedIn" );
}
}
private readonly IUser innerUser;
public UserObservableWrapper( IUser nonObservableUser )
{
this.innerUser = nonObservableUser;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged( string propertyName )
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler( this, new PropertyChangedEventArgs( propertyName ) );
}
}
}
There must be a more intelligent way of doing that!?
If it doesn't happen a lot of times in your code I would recommend you to do the boilerplate code.
Otherwise, you can use this cool piece of code from Ayende to generate a proxy class that would auto implement INotifyPropertyChanged for you (including event raising).
Usage would look like this:
IUser userProxy = DataBindingFactory.Create<User>();

C# and property change notifications

Why does C# make me do this:
public class SomeClass {
public event PropertyChangedEventHandler Changed;
public void OnChanged(object sender, PropertyChangedEventArgs e)
{
if (Changed != null)
Changed(sender, e);
}
[XmlIgnore]
private string _name;
public string Name {
get { return _name; }
set
{
_name = value;
OnChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
intead of something like this:
[GeneratesChangeNotifications]
public class SomeClass {
[GeneratesChangeNotification]
public string Name { get; set; }
}
I know you can do this with PostSharp and other 3rd party libraries... but something so integral and otherwise error prone (e.g. misspelling the name in the string), I think, should be built into the language... why doesn't Microsoft do this?... is there some purist language reason why this doesn't happen. It's a common enough need.
So far here is the best that I've come up with... I've written a class called NotifyPropertyChanged... it handles the property name, the test for if it's changed or not, the update of the local variable, and the notification of the change... and the SetProperty function is generic, so one function will work with all types of properties... works pretty well.
(Also notice you can call OnPropertyChanged with several property names if you like.)
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(params string[] props)
{
foreach (var prop in props)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
public bool SetProperty<T>(ref T oldValue, T newValue, [CallerMemberName]string prop = "")
{
if (oldValue == null && newValue == null)
return false;
if (oldValue != null && oldValue.Equals(newValue))
return false;
oldValue = newValue;
OnPropertyChanged(prop);
return true;
}
}
Then use it like this:
public class MyClass : NotifyPropertyChanged
{
public string Text { get => _text; set => SetProperty(ref _text, value); }
string _text;
}
My RaisePropertyChanged always uses the caller member name, or if needed reflects the property name passed in in an expression (ie, if I need to make one property notify for another). You cannot always guarantee that all properties can notify, and being able to notify more than one at a time is therefore useful.
In addition, with the standard model, using an event allows anyone to subscribe, which an attributed model can not.

Update the model from the view model

Update the model from the view model
I have read some post about the MVVM but I not sure if understand the
way that the view model is updating the model
Currently I have two text boxes in the UI which is bound to the XAML view and call to the view model when the event was raised .
when should be the place in the view model when I updating the model?
This is the view model
class ViewModel:INotifyPropertyChanged
{
private String _url;
private String _TemplateType;
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
The model
internal class DefineAddinModel
{
public string TemplateType { get; set; }
public String URL { get; set; }
}
The ViewModel usually acts as a wrapper around the Model and contains a reference to the Model which is can update either in response to commands or automatically in property setters.
UPDATE:
Here's an example of having the VM act as a wrapper around the Model. This may seem useless in your example but you will find in many cases the VM's getters/setters need to do some sort of transformation on the values rather than simply passing them through.
class ViewModel:INotifyPropertyChanged
{
private DefineAddinModel model;
public string URL
{
get { return model.URL; }
set
{
if (value != model.URL)
{
model.url = value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return model.TemplateType; }
set
{
if (value != model.TemplateType)
{
model.TemplateType = value;
OnPropertyChanged("TemplateType");
}
}
}
The better way to update your Model Is by using an event, its safer, so choose weather using a button click or lost focus, or whatever you want
void button_click(object sender,eventsarg e)
{
MyObj.URL = App.Locator.MyVM.MyDefineAddinModel.URL;// App.Locator because MVVMLight is tagged
MyObj.TemplateType = App.Locator.MyVM.MyDefineAddinModel.TemplateType ;
}
but personnaly i Use the following steps :
1- In your ViewModel create a CurrentItem object of type DefineAddinModel and without OnPropertyChanged then bind it to the View(UI) DataContext of the RootElement on the View )
2- for the model I use the INotifyPropertyChanged for each propery
3- after binding the datacontext of your root element to the CurrentItem of your ViewModel then bind just URL and TemplateType properties to your Controls, so any thing changes on the textbox will update CurrentItem properties
you can also chose the type of the binding (On LostFocus, or OnPropertyChanged)
You need to bind your TextBoxes to the two properties URL and TemplateType.
Try to use Commands (in the ViewModel)instead of events (in The CodeBehind) since you are in MVVM.
For updating the model : use a button with it's Command property bound to OnSave just like this example:
private String _url;
private String _TemplateType;
private DefineAddinModel _defineAddin;
public DefineAddinModel DefineAddin
{
get {return _defineAddin;}
set
{
_defineAddin = value;
OnPropertyChanged("DefineAddin");
}
}
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
public RelayCommand OnSaved
{
get;
set;
}
public ViewModel()
{
DefineAddin = new DefineAddinModel();
OnSaved = new RelayCommand(()=>
{
DefineAddin.URL = URL ;
DefineAddin.TemplateType = TemplateType;
});
Think about using third parties like MVVMLight it helps you a lot with MVVM and the helpers around it (Commands, Messenger, ViewModelLocator ...)
I think that the correct answer here is 'it depends'.
In most general cases, the advantage of actually using a ViewModel is also to track 'transient state', i.e. the state of an 'edit in progress' operation.
In this particular case, you would not push your changes directly to the Model every time a value is updated, instead you would do this via an 'Update' ICommand implementation that will collect all the data from the ViewModel and push it down to the Model.
This approach gives you many advantages:
The user of the view can change their mind as many times as they want, and only when they are happy will the Model actually get updated with their definitive choices
It greatly reduces the load on your persistence service, since only final changes are pushed through.
It allows you to do final validation on a complete set of values, rather than transient states, and hence reduces programming complexity and overhead.
It also makes your UI far more fluid since all the examples above are pushing updates on the UI Dispatcher, and avoids you having to cater for this via Tasks or other async approaches.
The backing model is never in an inconsistent state, since I would imagine that all values on one View/ViewModel are related, and only make sense when updated together using an ACID approach.
Here's an example of how I'd do it.
public class ViewModel:INotifyPropertyChanged {
private String _url;
private String _TemplateType;
public ViewModel(){
UpdateCommand = new DelegateCommand(OnExecuteUpdate, OnCanExecuteUpdate);
}
public bool OnCanExecuteUpdate(object param){
// insert logic here to return true when one can update
// or false when data is incomplete
}
public void OnExecuteUpdate(object param){
// insert logic here to update your model using data from the view model
}
public ICommand UpdateCommand { get; set;}
public string URL{
get { return _url; }
set {
if (value != _url) {
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType {
get { return _TemplateType; }
set {
if (value != _TemplateType) {
_TemplateType= value;
OnPropertyChanged("TemplateType");
}
}
}
... etc.
}
public class DelegateCommand : ICommand {
Func<object, bool> canExecute;
Action<object> executeAction;
public DelegateCommand(Action<object> executeAction)
: this(executeAction, null) {}
public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute) {
if (executeAction == null) {
throw new ArgumentNullException("executeAction");
}
this.executeAction = executeAction;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter) {
bool result = true;
Func<object, bool> canExecuteHandler = this.canExecute;
if (canExecuteHandler != null) {
result = canExecuteHandler(parameter);
}
return result;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() {
EventHandler handler = this.CanExecuteChanged;
if (handler != null) {
handler(this, new EventArgs());
}
}
public void Execute(object parameter) {
this.executeAction(parameter);
}
}

Categories