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)
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.
Searching didn't bring me any clues and I am sort of at a loss.
WPF is self taught so far, so I might be overlooking something simple.
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<TextBlock Text={Binding BoundTextProperty}"/>
that's the simplified xml
public class MainViewModel
{
private Model Data;
public MainViewModel()
{...}
public string BoundTextProperty => Data.BoundTextProperty;
...
}
The Property that's bound referencing the Property holding the Data in the model
public class Model : INotifyPropertyChanged
{
private long number;
public long Number
{
get { return number; }
set
{
number = value;
OnPropertyChanged(nameof(BoundTextProperty));
}
}
public string BoundTextProperty => $"Some text {Number} some text again";
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I swear it worked at some point.
The string has a couple other variables, but that's the basic of how it works or rather doesn't.
My Question is wether or not the Binding can actually bubble up, and if it can, why doesn't it?
You have to add the code for bubbling up the Model's PropertyChanged event from the ViewModel to the View.
Here is an example (based on your code):
public class MainViewModel : ViewModelBase
{
private readonly Model Data;
public MainViewModel()
{
Data = new Model();
Data.PropertyChanged += ModelOnPropertyChanged;
}
private void ModelOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(Model.BoundTextProperty):
OnPropertyChanged(nameof(MainViewModel.BoundTextProperty));
break;
// add cases for other properties here:
}
}
public string BoundTextProperty => Data.BoundTextProperty;
}
public class Model : ModelBase
{
private long number;
public long Number
{
get { return number; }
set
{
number = value;
OnPropertyChanged(nameof(BoundTextProperty));
}
}
public string BoundTextProperty => $"Some text {Number} some text again";
}
public abstract class ViewModelBase : Base
{
// add other ViewModel related stuff here
}
public abstract class ModelBase : Base
{
// add other Model related stuff here
}
public abstract class Base : INotifyPropertyChanged
{
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
You need to raise the PropertyChanged event for the source property that you bind to in your XAML. In this case you bind to the BoundTextProperty of the MainViewModel which means that the MainViewModel class should raise the PropertyChanged event.
It doesn't matter whether the source property wraps another property of a class that does raise the PropertyChanged event. It's the source object of the binding that notifies the view.
You could also just bind to the "model" property directly, provided that you turn Data into a public property in your view model:
public Model Data { get; private set; }
...
<TextBlock Text="{Binding Data.BoundTextProperty}"/>
If you choose to stick with your wrapper property, the MainViewModel must implement the INotifyPropertyChanged event and raise the PropertyChanged event whenever the model is updated:
public class MainViewModel : INotifyPropertyChanged
{
private readonly Model Data;
public MainViewModel()
{
Data = new Model();
Data.PropertyChanged += Data_PropertyChanged;
}
private void Data_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnPropertyChanged("BoundTextProperty");
}
public string BoundTextProperty => Data.BoundTextProperty;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have a List which contains a few items of this class:
public class Entry
{
public string FileSize;
public string Name;
public byte[] Data;
public UInt64 XXHashFilePath;
public UInt32 FileDataOffset;
public UInt32 CompressedSize;
public UInt32 UncompressedSize;
public CompressionType TypeOfCompression;
public UInt64 SHA256;
public Entry(BinaryReader br)
{
XXHashFilePath = br.ReadUInt64();
FileDataOffset = br.ReadUInt32();
CompressedSize = br.ReadUInt32();
UncompressedSize = br.ReadUInt32();
TypeOfCompression = (CompressionType)br.ReadUInt32();
SHA256 = br.ReadUInt64();
FileSize = Functions.SizeSuffix(UncompressedSize);
}
}
I want my ListBox to display XXHashFilePath, TypeOfCompression, FileSize, Name in columns in that specific order.
You have change your fields to properties (like Clemens proposed). This is also a principle of OOP with c# https://msdn.microsoft.com/en-us/library/ms229057(v=vs.110).aspx
Furthermore you may want to implement the INotifyPropertyChanged is just necessary when properties change their value after an Entry was added to the items collection of a ListBox. If Entry elements are immutable and just added to (and removed from) a ListBox, you won't have to implement INotifyPropertyChanged.
public class EntryViewModel : ViewModelBase
{
private string _FileSize;
public string FileSize
{
get
{
return _FileSize;
}
set
{
_FileSize = value;
OnPropertyChanged();
}
}
}
It's also a best practice to use the MVVM pattern. Here is a simple base class that should get you started without using a MVVM-framework.
public class ViewModelBase : INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion Events
#region Methods
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion Methods
}
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.