How do I raise PropertyChanged for SomeProperty in class B?
This example does not compile since PropertyChanged is not accessible this way...
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public class B : A
{
private object _someProperty;
public object SomeProperty
{
get => _someProperty;
set
{
_someProperty = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SomeProperty)))
}
}
}
Solution 1:
You can use this RaisePropertyChangedExtension:
public static class RaisePropertyChangedExtension
{
public static void RaisePropertyChanged(this INotifyPropertyChanged #this, [CallerMemberName] string propertyName = null)
{
var declaringType = #this.GetType().GetEvent(nameof(INotifyPropertyChanged.PropertyChanged)).DeclaringType;
var propertyChangedFieldInfo = declaringType.GetField(nameof(INotifyPropertyChanged.PropertyChanged), BindingFlags.Instance | BindingFlags.NonPublic);
var propertyChangedEventHandler = propertyChangedFieldInfo.GetValue(#this) as PropertyChangedEventHandler;
propertyChangedEventHandler?.Invoke(#this, new PropertyChangedEventArgs(propertyName));
}
}
Like this:
public class B : A
{
private object _someProperty;
public object SomeProperty
{
get => _someProperty;
set
{
_someProperty = value;
this.RaisePropertyChanged();
}
}
}
In my opinion this is the best solution I know so far.
Disadvantage is that you're able to raise PropertyChanged from another class like this:
public class C
{
public C(B b)
{
b.RaisePropertyChanged(nameof(b.SomeProperty));
}
}
It's not good practise to raise PropertyChanged from other classes this way, so i'm not concerned by this disadvantage.
This solution is inspired by Thomas Levesque's answer here: Simple small INotifyPropertyChanged implementation
Solution 2:
You can create a protected RaisePropertyChanged in the base class A:
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And call the method in the derived class B:
public class B : A
{
private object _someProperty;
public object SomeProperty
{
get => _someProperty;
set
{
_someProperty = value;
RaisePropertyChanged();
}
}
}
Disadvantage is that you have to implement the RaisePropertyChanged method for each new base class you're creating on the opposite you avoid the disadvantage that Solution 1 had.
Related
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.
I'm getting a null exception, but the field was initialized as an empty list. So how could it be null?
The error occurs on the second line in this method (on _hydratedProperties):
protected virtual void NotifyPropertyChanged<T>(Expression<Func<T>> expression)
{
string propertyName = GetPropertyName(expression);
if (!this._hydratedProperties.Contains(propertyName)) { this._hydratedProperties.Add(propertyName); }
}
And this is how the field is declared:
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class
{
private List<string> _hydratedProperties = new List<string>();
This is how it's set:
public Eta Eta
{
get { return this._eta; }
set
{
this._eta = value;
NotifyPropertyChanged(() => this.Eta);
}
}
This is the full class (with the comments and non-relevant parts removed):
[DataContract]
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class
{
private List<string> _hydratedProperties = new List<string>();
public bool IsPropertyHydrated(string propertyName)
{
return this._hydratedProperties.Contains(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged<T>(Expression<Func<T>> expression)
{
string propertyName = GetPropertyName(expression);
if (!this._hydratedProperties.Contains(propertyName)) { this._hydratedProperties.Add(propertyName); }
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public string GetPropertyName<T>(Expression<Func<T>> expression)
{
MemberExpression memberExpression = (MemberExpression)expression.Body;
return memberExpression.Member.Name;
}
}
Derived class:
[DataContract]
public class Bin : EntityBase<Bin>
{
private Eta _eta;
[DataMember]
public Eta Eta
{
get { return this._eta; }
set
{
this._eta = value;
NotifyPropertyChanged(() => this.Eta);
}
}
}
Here's the clue:
[DataContract]
Yup. DataContractSerializer does not call any constructor. Instead, it uses FormatterServices.GetUninitializedObject to create the object that will be deserialized. This bypasses the constructor call.
Your initializer:
private List<string> _hydratedProperties = new List<string>();
is translated to an implicit default constructor by the compiler.
As a workaround, you can use a deserialization callback with OnDeserializingAttribute:
[DataContract]
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged
where TSubclass : class
{
private List<string> _hydratedProperties;
protected EntityBase()
{
Init();
}
private void Init()
{
_hydratedProperties = new List<string>()
}
[OnDeserializing]
private void OnDeserializing(StreamingContext context)
{
Init();
}
// ... rest of code here
}
I found a simpler answer than what Lucas provided. I'm not sure if this one is actually better, but it's simple and it worked. All I did was add the DataMember attribute to the field. Since that specifies that the field is part of the data contract, it is included with serialization/deserialization and no longer causes a null reference error.
[DataContract]
public abstract class EntityBase<TSubclass> : INotifyPropertyChanged where TSubclass : class
{
[DataMember]
private List<string> _hydratedProperties = new List<string>();
// More code here
}
I have this Base class:
public class MyFileInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _file;
private int _bytesSent;
public MyFileInfo(string file)
{
}
public string File
{
get { return _file; }
set { _file = value; }
}
public int BytesSent
{
get { return _bytesSent; }
set
{
_bytesSent = value;
OnPropertyChanged("BytesSent");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And the derive class:
public class MyFile : MyFileInfo
{
public MyFile(MyFileInfo myFileInfo)
}
this.File = pcapInfo.myFileInfo;
this.BytesSent = pcapInfo.BytesSent;
}
public DoWork()
{
// here BytesSent is changing
}
{
OK so i have the base and the derive class.
Inside the derive class my property BytesSent is changing but my UI not.
This is my Collection:
private ObservableCollection<MyFile> files{ get; set; }
Maybe i need to define the OnPropertyChanged method in the derive class ?
ObservableCollection doesn't notify when properties within the items change. It will only notify you when the list it self changes, such as if an item was added or removed.
Look here for more info: ObservableCollection not noticing when Item in it changes (even with INotifyPropertyChanged)
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.
I have a class that implements INotifyPropertyChanged.
I create an instance of a class in some viewModel.
Is it possible to remove this functionality from the class and inject it after the instance was created? I heard that ICustomTypeDescriptor would make this happen, but i dont know how to use it.
public class C : ICustomNotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int _id;
public string _name;
public int Id
{
get { return _id; }
set
{
if (_id == value)
{
return;
}
_id = value;
OnPropertyChanged("Id");
}
}
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged("Name");
}
}
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
If you are just trying to prevent the notifications from being fired when the object is first created and properties set, you can add boolean flag(s) that is/are false until the properties have been set once. You only execute the notification if the flag is true.
Edit:
I don't think there's a clean way to get the functionality in there after removing all the INotifyPropertyChanged code, but there are many ways to control the functionality from outside the instance.
Please note that I wrote all this code in the text editor, not in VisualStudio; it has not been tested in any way.
Add a method to enable notifications:
public class OptionalNotification : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string name) ...
bool _shouldNotify;
public void EnableNotifications()
{
_shouldNotify = true;
}
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return
_someProperty = value;
if(_shouldNotify) OnPropertyChanged("SomeProperty");
}
}
}
You could do the same thing without the method, if you knew at the time of instantiation whether or not the instance should produce notifications, in which case you'd just need a boolean parameter in the constructor.
Another variation would be to use the Factory pattern, where your Factory has internal access to the boolean flag and sets it upon construction.
Encapsulate the condition in a proxy:
public interface IEntity : INotifyPropertyChanged
{
string SomeProperty { get; set; }
}
public class Entity : IEntity
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name) ...
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return
_someProperty = value;
OnPropertyChanged("SomeProperty");
}
}
}
public class EntityNotificationProxy : IEntity
{
IEntity _inner;
public EntityNotificationProxy(IEntity entity)
{
_inner = entity;
_inner.PropertyChanged += (o,e) => { if(ShouldNotify) OnPropertyChanged(o,e); }
}
public bool ShouldNotify { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(object sender, PropertChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null) handler(sender, e);
}
public string SomeProperty
{
get { return _inner.SomeProperty; }
set
{
if(_inner.SomeProperty == value) return
_inner.SomeProperty = value;
}
}
}
Here your consuming classes get the entity proxy instead of the entity itself (but is none the wiser because it references only IEntity when you program to interfaces/abstractions). The wrapping of the proxy can happen in a factory or through an IoC container/DI framework.
The main advantage to this approach is that your entity maintains a pure INotifyPropertyChanged implementation, and the conditional aspect is handled from without. Another advantage is that it helps to enforce programming to abstractions and inversion of control.
The main disadvantage is that you'll need to create proxies for each INotifyPropertyChanged implementation that you want to have this conditional behaviour.
Create a registry to keep track of what instances should or should not raise notifications:
public static class PropertyNotificationRegistry
{
static IDictionary<INotifyPropertyChanged, bool> _registeredClasses
= new Dictionary<INotifyPropertyChanged, bool>;
static void Register(INotifyPropertyChanged o, bool shouldNotify)
{
if(!(_registeredClasses.ContainsKey(o)) _registeredClasses.Add(o, shouldNotify);
// could also implement logic to update an existing class in the dictionary
}
public static void ShouldNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
{
Register(o, true);
}
public static void ShouldNotNotifyWhenPropertiesChange(this INotifyPropertyChanged o)
{
Register(o, false);
}
public static void NotifyPropertyChanged(this INotifyPropertyChanged o, Action notificationAction)
{
if(_registeredClasses.ContainsKey(o))
{
bool shouldNotify = _registeredClasses.Where(x => x.Key == o).Single().Value;
if(shouldNotify) notificationAction();
}
}
}
public class EntityUsingNotificationRegistry : INotifyPropertyChanged
{
... // all the standard INotifyPropertyChanged stuff
string _someProperty;
public string SomeProperty
{
get { return _someProperty; }
set
{
if(_someProperty == value) return;
_someProperty = value;
this.NotifyPropertyChanged(() => OnPropertyChanged("SomeProperty"));
}
}
}
public class SomethingInstantiatingOurEntity
{
public void DoSomething()
{
var entity1 = new EntityUsingNotificationRegistry();
entity1.ShouldNotifyWhenPropertiesChange();
var entity2 = new EntityUsingNotificationRegistry();
entity2.ShouldNotNotifyWhenPropertiesChange();
entity1.SomeProperty = "arbitrary string"; // raises event
entity2.SomeProperty = "arbitrary string"; // does not raise event
var entity3 = new EntityUsingNotificationRegistry();
entity3.SomeProperty = "arbitrary string"; // does not raise event
entity3.ShouldNotifyWhenPropertiesChange();
entity3.SomeProperty = "another arbitrary string"; // now raises event
}
}
Now, the registry has a distinct shortcoming in that it holds references to every instance and will prevent those instances from being picked up by the garbage collector. There may be a solution to this by implementing the registry with WeakReferences, but I'm not up-to-snuff on their usage to recommend a particular implementation.
This will not work. You COULD subclass and inject it, but you would have to change the byte-code to make sure the proper methods are CALLED - and that is the harder method.