This question already has answers here:
How to check if an object has changed?
(7 answers)
Closed 4 years ago.
Is it possible to know when an property is modified within the entity itself?
E.g.:
public class StudentEntity{
public string studentId { get; set; }
public string studentStatus { get; set; }
public string getStatusChangeDate{
get
{
//if studentStatus change then return date
}
}
}
The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.
For example, consider a Person object with a property called FirstName. To provide generic property-change notification, the Person type implements the INotifyPropertyChanged interface and raises a PropertyChanged event when FirstName is changed.
For change notification to occur in a binding between a bound client and a data source, your bound type should either:
Implement the INotifyPropertyChanged interface (preferred).
Provide a change event for each property of the bound type.
Rewrite your code:
public class StudentEntity : INotifyPropertyChanged
{
private string studentIdValue;
public string StudentId
{
get { return this.studentIdValue; }
set
{
if(value != this.studentIdValue)
{
this.studentIdValue = value;
this.OnPropertyChanged(nameof(this.StudentId));
}
}
}
private string studentStatusValue;
public string StudentStatus
{
get { return this.studentStatusValue; }
set
{
if(value != this.studentStatusValue)
{
this.studentStatusValue= value;
this.OnPropertyChanged(nameof(this.StudentStatus));
}
}
}
public string StatusChangeDate { get; set; }
public StudentEntity(string studentId, string studentStatus)
{
// Don't invoke property-setters from the ctor to avoid raising the event prematurely), instead set the backing fields directly:
this.studentIdValue = studentId;
this.studentStatusValue = studentStatus;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
class Program
{
static void Main(string[] args)
{
var person = new StudentEntity("101", "Accept");
person.PropertyChanged += Person_PropertyChanged;
person.StudentStatus = "Reject";
Console.ReadLine();
}
private static void Person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
StudentEntity studentEntity = sender as StudentEntity;
if (e.PropertyName == "StudentStatus")
{
studentEntity.getStatusChangeDate = DateTime.Now.ToString();
}
}
}
You can use a method to set the value. This means each time EF loads the record, it won't get overwritten: but of course you have to remember to call the method and not set the property directly.
public class StudentEntity {
public string studentId { get; set; }
public string studentStatus { get; set; }
public DateTime studentStatusChanged { get; set; }
public void SetStudentStatus(string status) {
studentStatus = status;
studentStatusChanged = DateTime.Now;
}
}
Example: https://dotnetfiddle.net/kF8VZR
Related
I have two buttons and bind their property to two properties of a data object.
But every property is updated when I call PropertyChanged of the data object.
public partial class Form1 : Form
{
private DataClass data = new DataClass();
public Form1()
{
InitializeComponent();
ButtonA.DataBindings.Add("Text", data, "DataA");
ButtonB.DataBindings.Add("Text", data, "DataB");
ButtonB.Click += new EventHandler(OnButtonBClicked);
}
private void OnButtonBClicked(object sender, EventArgs e)
{
data.DataA += "1";
data.DataB += "1";
data.Notify("DataB");
}
}
public class DataClass : INotifyPropertyChanged
{
public string DataA { get; set; }
public string DataB { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public DataClass() {}
public void Notify(string property_name)
{
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
}
When I press ButtonB (which means I call PropertyChanged(this, new PropertyChangedEventArgs("DataB"))), both ButtonA and ButtonB show new text.
If I call PropertyChanged(this, new PropertyChangedEventArgs("DataA")), both buttons are updated.
If I don't change value of DataA / DataB and just call PropertyChanged(this, new PropertyChangedEventArgs("DataB")), still both buttons are updated (can be noticed by breakpoint debugging).
If I call PropertyChanged(this, new PropertyChangedEventArgs("QQQ")), then no button is updated.
PropertyChangedEventArgs has a property named propertyName, I thought it's used to specify one property to notify but it doesn't.
In my real code, DataB changes much more frequently than DataA. I don't want to update ButtonA each time DataB is changed, it takes too much time.
Question: why would this happen? When a data source property is changed, how can I only update properties really connected to it?
(All code is .Net Framework 4.7.1 on Windows.)
#Jimi's method works.Simple and effective.I put each property in a shell class and bind data to the shell:
public class MyProperty<T>: INotifyPropertyChanged
{
public T Content { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public MyProperty(T _content)
{
Content = _content;
}
public void Notify()
{
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public class DataClass
{
public MyProperty<string> DataA = new MyProperty<string>("");
public MyProperty<string> DataB = new MyProperty<string>("");
public DataClass() {}
}
But in this way I must use DataA.Content+="1" instead of DataA+="1" every where.
I decide to use a base class to create all shells.But my real DataClass must inherit from other class and C# don't support multi-inherit.So I have to use a extension class.
public class BindHandle<T> : INotifyPropertyChanged
{
public T Content { get { return (T)parent.GetType().GetProperty(prop_name).GetValue(parent); } }
private object parent;
private string prop_name;
public event PropertyChangedEventHandler PropertyChanged;
public BindHandle(object _parent, string _prop_name)
{
parent = _parent;
prop_name = _prop_name;
}
public void NotifyChange()
{
PropertyChanged(this, new PropertyChangedEventArgs("Content"));
}
}
public interface IBindHandleProvider
{
BindHandleProvider provider { get; set; }
}
public class BindHandleProvider
{
private Dictionary<string, object> handle_map = new Dictionary<string, object>();
public BindHandle<T> GetBindHandle<T>(object obj,string property_name)
{
if (!handle_map.ContainsKey(property_name))
handle_map.Add(property_name, new BindHandle<T>(obj, property_name));
return (BindHandle<T>)handle_map[property_name];
}
public void NotifyChange<T>(string property_name)
{
if (handle_map.ContainsKey(property_name))
((BindHandle<T>)handle_map[property_name]).NotifyChange();
}
}
public static class BindHandleProviderExtension
{
public static void NotifyChange<T>(this IBindHandleProvider obj, string property_name)
{
obj.provider.NotifyChange<T>(property_name);
}
public static BindHandle<T> GetBindHandle<T>(this IBindHandleProvider obj, string property_name)
{
return obj.provider.GetBindHandle<T>(obj,property_name);
}
}
public class DataClass:IBindHandleProvider
{
public BindHandleProvider provider { get; set; } = new BindHandleProvider();
public string DataA { get; set; } = "";
public string DataB { get; set; } = "";
public DataClass(){ }
}
Then bind it like
ButtonA.DataBindings.Add("Text", data.GetBindHandle<string>("DataA"), "Content");
And notify like
data.NotifyChange<string>("DataB");
It's kinda complex but works well.
I have an observable collection that is shared between different viewmodels.
public class UserInput1ViewModel: INotifyPropertyChanged
{
public ObservableCollection<ParamClass> ParamColl { get; set; }
public UserInput1ViewModel(<ParamClass> paramColl)
{
this.ParamColl = paramColl;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
private void UpdateCollection()
{
this.ParamList = PerformCalculations();
}
}
public class ParamClass
{
public double Property1 { get; set; }
public double Property2 { get; set; }
public double Property3 { get; set; }
... ...
... ...
public double Property19 { get; set; }
}
The function PerformCalculations() will execute, but it will not update the all the properties inside the observable collection. I have learned that you cannot do that with observable collection https://stackoverflow.com/a/9984424/4387406.
So, this is what I am currently doing.
private void UpdateCollection()
{
var output = PerformCalculations();
for(int i = 0; i < output.Count(); i++)
{
this.ParamColl[i].Property1 = output[i].Property1;
this.ParamColl[i].Property2 = output[i].Property2;
... ...
... ...
this.ParamColl[i].Property19 = output[i].Property19;
}
}
My question is: is there a better way of sharing observable collection?
Many thanks in advance.
If you want the GUI to update whenever a property of an instance in a list changes, you should implement the INotifyPropertyChanged in the instance class, just as you have done in your ViewModel.
You haven't shown what your ParamClass looks like so I'm using a Person class in place. You could do the same thing as you've done in your ViewModel.
public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int age;
public int Age
{
get { return age; }
set { age = value; OnPropertyChanged("Age"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
Now, if even a single property in any of the instances is changed it will be reflected on your GUI.
Since you're using WPF, there's quite a few good MVVM tool-kits out there that will do a lot of this for you. For instance, MVVM Light Toolkit is one such examples. There's many others out there.
I have the following classes
public abstract class Contact
{
public abstract string FullName { get; }
public abstract string FullName_LastNameFirst { get; }
}
public class PersonContact
{
private string firstName;
private string lastName;
public override string FullName => firstName + " " + lastName;
public override string FullName_LastNameFirst => lastName + ", " + firstName;
}
public class BusinessContact
{
private string name;
public override string FullName => name;
public override string FullName_LastNameFirst => name;
}
These classes extend INotifyPropertyChanged (not shown) and include public properties wrapping the private variables that trigger OnPropertyChanged.
The question is if I bind in WPF to the FullName or FullName_LastNameFirst properties how can I have them update when either of the properties that they are wrapping are changed.
When you change underlying private fields (firstName, lastName or name in BusinessContact) - call
OnPropertyChanged("FullName");
OnPropertyChanged("FullName_LastNameFirst");
WPF data binding will subscribe to PropertyChanged event of your object and will listen to change notifications of corresponding property. Since your property has no setter in which you can call OnPropertyChanged - you need to call it explicitly when any underlying data changes.
You surely have properties for your Person. If you have a ForeName property you can implement it like:
private string _ForeName;
public string ForeName
{
get
{ return _ForeName; }
set
{
if (_ForeName != value)
{
_ForeName = value;
OnPropertyChanged(nameof(this.ForeName));
OnPropertyChanged(nameof(this.FullName));
}
}
}
As you can see, a PropertyChanged event will be fired, if the value of ForeName is changed. You can make the LastName property similar.
An other solution is, if the person class listens to its own PropertyChanged:
public Person()
{
this.PropertyChanged += this.Person_PropertyChanged;
}
private void Person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(LastName))
{
this.OnPropertyChanged(nameof(FullName));
}
if (e.PropertyName == nameof(ForeName))
{
this.OnPropertyChanged(nameof(FullName));
}
}
This second solution is useful if you have generated classes, and you cannot change the property setters. Then you can make a partial class, and handle the property changes.
The answers from Evk and lvoros are technically correct.
However, you can save yourself from having to write all this boilerplate code by using the Fody ProperyChanged library (available via Nuget).
A class written as
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string GivenNames { get; set; }
public string FamilyName { get; set; }
public string FullName => $"{GivenNames} {FamilyName}";
}
Will actually be compiled as
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string givenNames;
public string GivenNames
{
get => givenNames;
set
{
if (value != givenNames)
{
givenNames = value;
OnPropertyChanged("GivenNames");
OnPropertyChanged("FullName");
}
}
}
string familyName;
public string FamilyName
{
get => familyName;
set
{
if (value != familyName)
{
familyName = value;
OnPropertyChanged("FamilyName");
OnPropertyChanged("FullName");
}
}
}
public string FullName => $"{GivenNames} {FamilyName}";
public virtual void OnPropertyChanged(string propertyName)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Fody will automatically generate OnPropertyChanged() calls for any dependent calculated properties in addition to the direct property wrappers.
I'm not normally a fan of programs working by "magic", but Fody is my one expception.
I'm having trouble with a simple image source binding.
I have a class that store the path to the image file (and other stuff) which look like this:
public class Ekta {
...
public string PATHMED { get; set; }
public string FICMED { get; set; }
public string FULLPATH { get { return PATHMED + FICMED; } }
...
}
I have the following property in my window:
public Ekta mainImg { get; set; }
And in the xaml, the binding is done like this:
<Image Source="{Binding Path=mainImg.FULLPATH}"/>
This work well when I set mainImg's value the first time (Before InitializeComponent() is called), but when I update it (mainImg = e; where e is an instance of Ekta) the UI doesn't change.
Am I missing something ? Is it the right way to bind an image source to a custom item ?
I suggest to make a base class named Notifier and use it for any class which needs INotifyPropertyChanged implementation
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then
public class Ekta : Notifier
{
private string _PATHMED;
public string PATHMED
{
get { return _PATHMED; }
set
{
_PATHMED = value;
RaisePropertyChanged();
RaisePropertyChanged("FULLPATH");
}
}
private string _FICMED;
public string FICMED
{
get { return _FICMED; }
set
{
_FICMED = value;
RaisePropertyChanged();
RaisePropertyChanged("FULLPATH");
}
}
public string FULLPATH
{
get { return PATHMED + FICMED; }
}
}
So I was trying to create my own event for the initialization of a class called Car, which inherits from an Automobile object. Below is the same in C# code:
`
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Abc.Training.Artifacts;
namespace Abc.Training.Objects
{
public abstract class Automobile
{
public string Model { get; set; }
public string Manufacturer { get; set; }
public string YoM { get; set; }
}
public class Car : Automobile
{
public static event Delegates.ObjectInitHandler OnInit;
public string MarketSegment { get; set; }
public int BootSpace { get; set; }
public Car(string model, string manufacturer, string yom)
{
Model = model ;
Manufacturer = manufacturer;
YoM = yom;
ObjectInitEventArgs eArgs = new ObjectInitEventArgs();
eArgs.IsResidentObject = true;
eArgs.ObjectType = this.GetType();
if (OnInit != null) OnInit(this, eArgs);
}
}
}
`
The ObjectInitHandler and its args (the delegate type used here) is also created by me as:
`
public delegate void ObjectInitHandler(object sender, ObjectInitEventArgs e);
public class ObjectInitEventArgs:EventArgs
{
public Type ObjectType { get; set; }
public bool IsResidentObject { get; set; }
}
`
I am subscribing to the event as below:
`
Car.OnInit += new Delegates.ObjectInitHandler(Car_OnInit);//able to do this as event is static
Car c = new Car("Maruti", "Maruti", "2004");
void Car_OnInit(object sender, ObjectInitEventArgs e)
{
Console.WriteLine("Car object initialized");
}
`
I wanted to create an event OnInit for this class. However, if I put an instance event OnInit in the publisher (my Car class), I will have to initialize the class first before I can subscribe to this event. Since I would like to fire this event on initialization, this becomes a chicken and egg problem for me.
I solved it by creating a static event Object and doing the subscription before the object initialization as shown below (this is a snippet from the code above itself):
public static event Delegates.ObjectInitHandler OnInit;
However, in an ASP.NET application, this would mean if multiple users access this application, I will have the same delegate object that will have duplicate subscriptions of events (because its static), which is obviously not cool.
Is there a design pattern which I can follow to have the event also as an instance member but still I can subscribe to the event before instantiation?
I think you have to pass that function as a callback:
public class Car : Automobile
{
// public static event Delegates.ObjectInitHandler OnInit; remove this
public string MarketSegment { get; set; }
public int BootSpace { get; set; }
public Car(string model, string manufacturer, string yom,ObjectInitHandler OnInit) //add the callback as parameter.
{
Model = model ;
Manufacturer = manufacturer;
YoM = yom;
ObjectInitEventArgs eArgs = new ObjectInitEventArgs();
eArgs.IsResidentObject = true;
eArgs.ObjectType = this.GetType();
if (OnInit != null) OnInit(this, eArgs);
}
}
Pass a callback to the constructor when initializing an object:
Car c = new Car("Maruti", "Maruti", "2004",new Delegates.ObjectInitHandler(Car_OnInit));
void Car_OnInit(object sender, ObjectInitEventArgs e)
{
Console.WriteLine("Car object initialized");
}
Actually, I don't see a need for an initialization event in your code unless there are asynchronous operations inside your constructor.
here is a way to do it, it don't use statics, and i used Actions instead of event arguments. (you can use it your way!)
note that i passed the callback function to the object when creating!
class Program
{
static void Main(string[] args)
{
Car c = new Car("Maruti", "Maruti", "2004", Car_OnInit);
Console.WriteLine("Press a key to exit...");
Console.ReadKey();
}
static void Car_OnInit()
{
Console.WriteLine("Car object initialized");
}
}
public abstract class Automobile
{
public string Model { get; set; }
public string Manufacturer { get; set; }
public string YoM { get; set; }
}
public class Car : Automobile
{
public event Action OnInit;
public string MarketSegment { get; set; }
public int BootSpace { get; set; }
public Car(string model, string manufacturer, string yom, Action callBack)
{
this.OnInit += callBack;
Model = model;
Manufacturer = manufacturer;
YoM = yom;
if (OnInit != null) OnInit();
}
}
Also you can pass any argumenst if you want, just use Action<T> like Action<string> instead of Action. then your callback will be Car_OnInit(string)
There is no need (even if it were possible). You have everything you need.
Firstly, you wouldn't subscribe to the event each page load/per user. You would do it once.. in Application_Start for example.
Second.. you have everything you need in the event. Notice this line of code:
if (OnInit != null) OnInit(this, eArgs);
You pass this as the sender argument. this is an instance of Car. So, in your event.. you have the instance you care about:
void Car_OnInit(object sender, ObjectInitEventArgs e) {
var instance = sender as Car;
// use instance here.
}