Basic routine of NotifyPropertyChanged - c#

So lets say I have class:
SurfaceLoad.cs
public class SurfaceLoad : Load
{
private double force;
public double Force{ get => force; set{force = value;}}
public SurfaceLoad(double f){
Force = f;
}
public SurfaceLoad(){
Force = 0.0;
}
}
And I have my ViewModel:
SurfaceLoadViewModel.cs
public class SurfaceLoadViewModel{
private SurfaceLoad surfaceLoad = new SurfaceLoad();
public double Force{
get => surfaceLoad.Force;
set{
surfaceLoad.Force = value;
NotifyPropertChanged("Force");
}
}
public SurfaceLoadViewModel(){
}
}
As I already found out, in a good MVVM-manner I have to put all the accessors for my SurfaceLoad-members into the ViewModel, as the Model itself should not contain any interaction-logic.
Question:
Now I have multiple implementations of Load (SurfaceLoad, PointLoad, AreaLoad,...). All of those are a member of the class called LoadContainer, which is only used to manage a package of loads, which occur at the same time.
How do I efficiently manage all those types in a ViewModel? Do I have to wrap a Property around each and every value?

You need to implement INotifyPropertyChanged interface in SurfaceLoad class.
public class SurfaceLoad : Load, INotifyPropertyChanged
{
private double force;
public double Force
{
get { return force; }
set
{
force = value;
NotifyPropertyChanged("Force");
}
}
public SurfaceLoad(double f)
{
Force = f;
}
public SurfaceLoad()
{
Force = 0.0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
protected void NotifyPropertyChanged<T>(Expression<Func<T>> propertySelector)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
string propertyName = GetPropertyName(propertySelector);
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}

The first two comments have already helped me a lot.
In addition, I did some research and came to the conclusion that it is much more efficient to write the "NotifyPropertyChanged" handlers into the models, since this procedure does not work against the mvvm-principles!

Related

Copy EventHandler in inherited class

I'm developing an application in c# WPF.
I use a class PropertyChangedNotifier to manage INotifyPropertyChanged (see that link).
I use a classical ViewModelBase :
public class ViewModelBase : INotifyPropertyChanged, IRequestClose
{
public PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = _propertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In my MainViewModel : ViewModelBase I have a PropertyChangedNotifier<MainViewModel> working like this :
class MainViewModel : ViewModelBase
{
private PropertyChangedNotifier<MainViewModel> _notifier;
public new event PropertyChangedEventHandler PropertyChanged
{
add { _notifier.PropertyChanged += value; }
remove { _notifier.PropertyChanged -= value; }
}
public MainViewModel()
{
_notifier = new PropertyChangedNotifier<MainViewModel>(this);
}
protected new void OnPropertyChanged(string propertyName)
{
_notifier.OnPropertyChanged(propertyName);
}
}
But changes are not detected, when a value changes, my MainWindows doesn't refresh (without using PropertyChangedNotifier it works). I saw that system initiates windows by using WindowsBase.dll!System.ComponentModel.PropertyChangedEventManager.StartListening(object source) and then I saw that my ViewModelBase.PropertyChanged is not null when the constructor for MainViewModel is called.
Is it possible to make something like this :
public MainViewModel()
{
_notifier = new PropertyChangedNotifier<MainViewModel>(this);
_notifier.PropertyChanged = base.PropertyChanged;
}
And will that fix my bug ?
Edit:
PropertyChangeNotifier from link :
[AttributeUsage( AttributeTargets.Property )]
public class DepondsOnAttribute : Attribute
{
public DepondsOnAttribute( string name )
{
Name = name;
}
public string Name { get; }
}
public class PropertyChangedNotifier<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public PropertyChangedNotifier( T owner )
{
mOwner = owner;
}
public void OnPropertyChanged( string propertyName )
{
var handler = PropertyChanged;
if( handler != null ) handler( mOwner, new PropertyChangedEventArgs( propertyName ) );
List<string> dependents;
if( smPropertyDependencies.TryGetValue( propertyName, out dependents ) )
{
foreach( var dependent in dependents ) OnPropertyChanged( dependent );
}
}
static PropertyChangedNotifier()
{
foreach( var property in typeof( T ).GetProperties() )
{
var dependsOn = property.GetCustomAttributes( true )
.OfType<DepondsOnAttribute>()
.Select( attribute => attribute.Name );
foreach( var dependency in dependsOn )
{
List<string> list;
if( !smPropertyDependencies.TryGetValue( dependency, out list ) )
{
list = new List<string>();
smPropertyDependencies.Add( dependency, list );
}
if (property.Name == dependency)
throw new ApplicationException(String.Format("Property {0} of {1} cannot depends of itself", dependency, typeof(T).ToString()));
list.Add( property.Name );
}
}
}
private static readonly Dictionary<string, List<string>> smPropertyDependencies = new Dictionary<string, List<string>>();
private readonly T mOwner;
}
The problem is that when my MainViewModel is created, the base.PropertyChanged is null. The constructor is called, fields/properties initialized (some call OnPropertyChanged) and after the MainView.InitializeComponent() add a handler in base.PropertyChanged (I attached a debugger to see that). This adding is done in the ViewModelBase, ignoring the public new event PropertyChangedEventHandler PropertyChanged in MainViewModel.
I've the idea to check if base.PropertyChanged has been modify and copy it.
I've done this, I don't know if there is a good idea, what do you think ?
public class ViewModelBase : INotifyPropertyChanged, IRequestClose
{
protected event PropertyChangedEventHandler _propertyChanged;
protected bool propertyAdded { get; private set; }
public event PropertyChangedEventHandler PropertyChanged
{
add
{
_propertyChanged += value;
propertyAdded = true;
}
remove { _propertyChanged -= value; }
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = _propertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public Delegate[] Get_PropertyChanged()
{
isPropertyAdded = false;
Delegate[] delegates = new Delegate[0];
if (_propertyChanged != null)
{
delegates = _propertyChanged.GetInvocationList();
_propertyChanged = null;
}
return delegates;
}
}
and
class MainViewModel : ViewModelBase
{
private PropertyChangedNotifier<MainViewModel> _notifier;
public new event PropertyChangedEventHandler PropertyChanged
{
add { _notifier.PropertyChanged += value; }
remove { _notifier.PropertyChanged -= value; }
}
public MainViewModel()
{
_notifier = new PropertyChangedNotifier<MainViewModel>(this);
}
protected new void OnPropertyChanged(string propertyName)
{
if (isPropertyAdded)
{
foreach (Delegate d in Get_PropertyChanged())
{
_notifier.PropertyChanged += (PropertyChangedEventHandler)d;
}
}
_notifier.OnPropertyChanged(propertyName);
}
}
Seems like a convoluted solution to a simple NotifyPropertyChanged("dependentProperty"), although I do understand the desire to engineer fancy solutions. It would make sense if it had some performance/loc benefits, but it doesn't. It just adds complexity.

How to notify a bound element oh a value change when value derives from model?

I have the visibility of a progress bar bound to The following property within my viewmodel:
public string CalcProgVisibility
{
get
{
return Calculation.CalcProgVisibility;
}
set
{
}
}
Calculation is my model, which can change the value. When the value changes within the model, what do I need to do to make sure the view is aware of this change?
EDIT:
Here is the property within my model too. I am using onpropertychanged but its not making it to the view.
I am changing the value within the model, the view is bound to my viewmodel and the viewmodel si trying to return a value taken from the model. I am updating the value on the model, and cannot push the fact that it has updated the value all the way down to the view, I can only get the viewmodel to see it has changed...
I updated the entire code. I hope it's clear now.
Define your control BindingMode = TwoWay
<TextBox Visibility="{Binding Path=CalcProgVisibility, Mode=TwoWay}"...
and call the OnPropertyChanged method on the setter of the property in your view model and also in your model
//Model
public class Calculation : INotifyPropertyChanged
{
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
//ViewModel
public class ViewModel : INotifyPropertyChanged
{
public ViewModel(Calculation model)
{
this.CalcProgVisibility = model.CalcProgVisibility;
model.PropertyChanged += (s, e) => UpdateEntity(s as Calculation);
}
private void UpdateEntity(Calculation source)
{
CalcProgVisibility = source.CalcProgVisibility;
}
private string _calcProgVisibility;
public string CalcProgVisibility
{
get { return _calcProgVisibility; }
set
{
_calcProgVisibility = value;
OnPropertyChanged("CalcProgVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
RaisePropertyChanged(propertyName);
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Your Viewmodel has to implement the INotifyPropertyChanged Interface. To fire it in your case your viewmodel must also be aware of changes in your model object. So your model object could also implement INotifyPropertyChanged, or you use some form of the observer pattern.
If your model implements INotifyPropertyChanged, your viewmodel must manually register for this event and implement an handler. This could in turn trigger the PropertyChange event of the viewmodel then.
Another but in my opinion ugly way would be to scan (per timer or background thread) through your viemodel and check if a value changed since the last scan and then trigger a property changed event.
The first solution could look like this:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace StackOverflow
{
[TestClass]
public class IntegrationTest
{
[TestMethod]
public void NotifyPropertyChangeShouldFireOnViewModelWhenModelChanges()
{
//Arrange
Model model = new Model();
ViewModel sut = new ViewModel(model);
bool notifyPropertyChangeOnViewModelWasCalled = false;
sut.PropertyChanged += (sender, e) => { notifyPropertyChangeOnViewModelWasCalled = true; };
//Act
model.CalcValue = 4711;
//Assert
Assert.IsTrue(notifyPropertyChangeOnViewModelWasCalled, "NotifyPropertyChange was not fired on ViewModel");
}
}
public class ObjectWithNotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Model : ObjectWithNotifyPropertyChanged
{
private double calcValue;
public double CalcValue
{
get
{
return calcValue;
}
set
{
if (calcValue != value)
{
calcValue = value;
RaisePropertyChanged();
}
}
}
}
public class ViewModel : ObjectWithNotifyPropertyChanged
{
public ViewModel(Model model)
{
this.model = model;
model.PropertyChanged += model_PropertyChanged;
}
void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "CalcValue":
RaisePropertyChanged("CalcValue");
break;
}
}
private Model model;
public double CalcValue
{
get
{
return model.CalcValue;
}
}
}
}

Property Change event is null even after I registered it

I use INotifyPropertyChanged to notify class when there is any change in a variable of a particular object within it.
Below is the class:
public class MyClass
{
public SecClass MyObj { get; set; }
//A few more variables
}
SecClass:
public class SecClass:INotifyPropertyChanged
{
private bool _noti= false;
public bool Noti
{
get { return _noti; }
set
{
_noti= value;
NotifyPropertyChanged("Noti");
}
}
//A few more variables
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Here my function that makes the event registration:
public void Register()
{
MyObj.PropertyChanged += MyObj_PropertyChanged;
}
Function works and the registration is done, but when it comes to change it displays the Property Change as null (I guess that somewhere registration deleted, before happens change, how can I check this?)
I hooked this together with:
static class Program
{
static void Main()
{
var c = new MyClass();
c.MyObj = new SecClass();
c.Register();
c.MyObj.Noti = !c.MyObj.Noti;
}
}
adding (for illustration):
private void MyObj_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}
to MyClass, and:
public event PropertyChangedEventHandler PropertyChanged;
to SecClass (to get them to compile), and it works fine - printing "Noti" at runtime. There is a theoretical thread-race, but it is very unlikely in any sane usage, but recommended usage is:
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
Also, for info: if you add [CallerMemberName] to that, you don't need to specify the property explicitly:
private void NotifyPropertyChanged([CallerMemberName] string name = null) {...}
with:
NotifyPropertyChanged(); // the compiler adds the "Noti" itself
But fundamentally: "cannot reproduce" - it works fine. I wonder if maybe it relates to your PropertyChanged implementation, since you don't actually show that. In particular, I wonder if you actually have two events: one explicitly implemented. That would mean that it is getting treated differently by your cast.

creating a ViewModel class

I would like to create a ViewModel Class to retrieve the values from the Database.
My goal is to retrieve the Values of Usage of RAM (Ram total & Ram available) from my DB Table and then display it on my View.
This is what I have done so far on my ViewModel Class
public class RamViewModel : INotifyPropertyChanged
{
float _ramTotal;
float _ramUsed;
public float RamTotal
{
get { return _ramTotal; }
set { _ramTotal = value; RaisePropertyChanged("RamTotal"); }
}
public float RamUsed
{
get { return _ramUsed; }
set { _ramUsed = value; RaisePropertyChanged("RamUsed"); }
}
private void RaisePropertyChanged(string p)
{
throw new NotImplementedException();
}
}
when I build the class, I got this error stating, " ViewModel.RamViewModel Does not implement interface member 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged'"
How to overcome this error
INotifyPropertyChanged is an interface with one member that needs to be included in your class definition:
public event PropertyChangedEventHandler PropertyChanged;
You should also change the code in RaisePropertyChanged to not throw an exception, by implementing the actual functionality:
private void RaisePropertyChanged(string p)
{
if (null != PropertyChanged) PropertyChanged(this, new PropertyChangedEventArgs(p));
}
Your class does not expose the PropertyChanged event, which is necessary for classes that implement INotifyPropertyChanged (it's the only member of that interface).
So you should add:
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName);
}
}
ObservableCollection is unrelated to this.

Calling OnPropertyChange("property_name") Sends Updates to All Bound Properties

So I have a class with 40 or so properties that are updated from communication with a micro controller. This class implements INotifyPropertyChanged.
Loose Example:
private int _Example;
public int Example
{
get
{
return _Example;
}
set
{
_Example = value;
OnPropertyChange("Example");
}
}
And the OnPropertyChange function:
protected void OnPropertyChange(string p_Property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(p_Property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Binding (many of these)
Second_Class_Control.DataBindings.Clear();
Second_Class_Control.DataBindings.Add("My_Property", FirstClass, "Example");
In the main form I've set up binds to display and react to these values. One of those happens to land on another property in a another class. I happened to place a breakpoint in the set function of this property, and noticed it was being called any time any property from the first class changed.
Is this the correct behavior? I don't notice any performance hits but I plan on having many instances of these classes running together and wasn't expecting this.
Thanks
Hmm.. I noticed that you have the your OnPropertyChange virtual. Why is this, are you making a override somewhere?
I usually creates it like this :
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
then for the usage :
public class MainWindowViewModel : ViewModelBase
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
}

Categories

Resources