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

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"); }
}
}

Related

uwp inherited property data binding not updating the UI

3 steps nested string property is not updating the UI. When I update the EvidenceName property it doesnt reflect on the UI right away until I navigate back and come again on this page in which case the viewmodel is initialized again.
I have a xaml page with following code :
<TextBlock Text="{x:Bind ViewModel.SelectedEvidence.EvidenceName, Mode=OneWay}" />
ViewModel property in code behind :
public EvidenceViewModel ViewModel { get; } = new EvidenceViewModel();
Selected Evidence property within the EvidenceViewModel :
public Evidence SelectedEvidence
{
get => _selectedEvidence;
set => Set(ref _selectedEvidence, value); //this calls for RaisePropertyChanged
}
EvidenceViewModel derives from Observable class for raising property changes.
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
EvidenceName property within the Evidence class
public string EvidenceName
{
get { return _evidenceName; }
set
{
if (_evidenceName != value)
{
_evidenceName = value;
RaisePropertyChanged();
}
}
}
Update 1
if I put a simple string property directly in EvidenceViewModel and bind the UI textblock to that string property than the changes reflect in real time as expected.
Update 2
After some further debugging I found out that any property which is being inherited by the class from parent class doesnt work well in binding, so EvidenceName property was actually coming from parent class EvidenceBase and was being inherited into child class Evidence.
Update 3
Code for Evidence class in Nswagger generated file for client
Code for EvidenceBase class
EvidenceName property which actually exists in EvidenceBase class
RasiePropertyChanged code in EvidenceBase
You could to let the Evidence class inherit from the Observable class and call the OnPropertyChanged method in EvidenceName.
For example:
public class Evidence:Observable
{
private string _evidenceName;
public string EvidenceName
{
get { return _evidenceName; }
set
{
if (_evidenceName != value)
{
_evidenceName = value;
OnPropertyChanged("EvidenceName");
}
}
}
}
Update:
I have tested the code from your Update 3, and I found that the problem is the overrides in Evidence class.
Please check the following code:
private void Button_Click(object sender, RoutedEventArgs e)
{
ViewModel.SelectedEvidence.EvidenceName = "testName";
}
public abstract partial class EvidenceBase : System.ComponentModel.INotifyPropertyChanged
{
private string _evidenceName;
[Newtonsoft.Json.JsonProperty("evidenceName",Required =Newtonsoft.Json.Required.Default,NullValueHandling =Newtonsoft.Json.NullValueHandling.Ignore)]
public string EvidenceName
{
get { return _evidenceName; }
set
{
if(_evidenceName!=value)
{
_evidenceName = value;
RaisePropertyChanged("EvidenceName");
}
}
}
protected virtual void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Evidence : EvidenceBase, System.ComponentModel.INotifyPropertyChanged
{
//Remove the override of PropertyChanged property and RaisePropertyChanged method to avoid hide the ones inherited from base class.
}
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class EvidenceViewModel:Observable
{
private Evidence _selectedEvidence;
public Evidence SelectedEvidence
{
get { return _selectedEvidence; }
set
{
Set(ref _selectedEvidence, value);
}
}
public EvidenceViewModel()
{
_selectedEvidence = new Evidence();
}
}
If the code cannot state your code about PropertyChanged exactly, please feel free to contact me.

structs and INotifyPropertyChanged

I'm trying to add properties to my Model playing with my first MVVM app.
Now I want to add a place to save specific data in a clean way, so I used a struct.
But I am having issues to notify property changed, it does not have access to the method (An object reference is required for the non-static field)
Can someone explain to me why this happens and inform me on a strategy that fit my needs?
Thanks!
public ObservableCollection<UserControl> TimerBars
{
get { return _TimerBars; }
set
{
_TimerBars = value;
OnPropertyChanged("TimerBars");
}
}
public struct FBarWidth
{
private int _Stopped;
public int Stopped
{
get { return _Stopped; }
set
{
_Stopped = value;
OnPropertyChanged("Name"); //ERROR: An object reference is required for the non-static field
}
}
private int _Running;
//And more variables
}
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
OnPropertyChanged needs to be defined in the scope that you wish to update properties on.
For that to work you'll have to implement the interface INotifyPropertyChanged.
And finally you have to provide the correct argument to the OnPropertyChanged method. In this example "Stopped"
public struct FBarWidth : INotifyPropertyChanged
{
private int _Stopped;
public int Stopped
{
get { return _Stopped; }
set
{
_Stopped = value;
OnPropertyChanged("Stopped");
}
}
private int _Running;
//And more variables
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
Edit: In your comment you mentioned that you've got a class sorounding the code you provided in your example.
That means you've nested a struct inside a class.
Just because you've nested your struct, doesn't mean it inherits properties and methods from the outer class. You still need to implement INotifyPropertyChanged inside your struct and define the OnPropertyChanged method inside it.

How to implement INotifyPropertyChanged for derived classes?

I have a base class:
public class PersonBaseClass : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set
{
if (value != name)
{
name = value;
NotifyPropertyChanged("Name");
}
}
}
}
and a derived class
public class TeacherClass : PersonBaseClass, INotifyPropertyChanged
{
private string id;
public string Id
{
get { return id; }
set
{
if (value != id)
{
id = value;
NotifyPropertyChanged("Id");
}
}
}
}
and this magic code at the end of each one above!
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then I show a list of Teachers collection in a list in xaml. Now if I change Id, changes will appear for user, but changes in Name which is a property in base class doesn't appear. In debug, I see after setting Name value, the handler inside NotifyPropertyChanged method is null which seems it is the problem.
How can I solve it to changes of base class also appear in the list?
Have only PersonBaseClass implementing INotifyPropertyChanged and make NotifyPropertyChange as protected so you can call it from child classes. There is not need to implement it twice. That should fix the problem as well.
Your "magic code" section should only be in PersonBaseClass. You can make the NotifyPropertyChanged function protected so that the same function can be called from TeacherClass as well.

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.

Having ObservableCollection Trigger an update

So i have something along the lines of
private ObservableCollection<ViewModel> _internal;
public ObservableCollection<ViewModel> BoundInternal{get;set}; //this is Binded in the Itemssource like ItemSource={Binding BoundInternal}
Now In my code i do something like
BoundInternal=_internal, However the problem is the BoundInternal isn't trigger any collectionChanged event. I have to use the Add method. So I am wondering if there is a solution to this.
Here is what I suspect your code ought to look like like (although its not quite a match for what you currently doing):-
public class YourClassHoldingThisStuff : INotifyProperyChanged
{
private ObservableCollection<ViewModel> _internal;
public ObservableCollection<ViewModel> BoundInternal
{
get { return _internal; }
set
{
_internal = value;
NotifyPropertyChanged("BoundInternal");
};
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new ProperytChangedEventArgs(name));
}
}
In this case the _internal field becomes the source of the value of BoundInternal directly and you should only assign it via BoundInternal, (don't assign a value directly to _internal). When that occurs anything currently bound to it will be informed of the change.
If for some reason you really do need to maintain _internal as a separate reference from the backing field of BoundInternal then:-
public class YourClassHoldingThisStuff : INotifyProperyChanged
{
private ObservableCollection<ViewModel> _internal;
private ObservableCollection<ViewModel> _boundInternal;
public ObservableCollection<ViewModel> BoundInternal
{
get { return _boundInternal; }
set
{
_boundInternal = value;
NotifyPropertyChanged("BoundInternal");
};
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new ProperytChangedEventArgs(name));
}
}
Now at some point in your code when you do BoundInternal = _internal, anything bound to it will be informed of the change.
Every ItemsControl has a, Items property which has a Refresh() method that you can call, which will update your list.
MyList.Items.Refresh()

Categories

Resources