List<string> INotifyPropertyChanged event - c#

I have a simple class with a string property and a List property and I have the INofityPropertyChanged event implemented, but when I do an .Add to the string List this event is not hit so my Converter to display in the ListView is not hit. I am guessing the property changed is not hit for an Add to the List....how can I implement this in a way to get that property changed event hit???
Do I need to use some other type of collection?!
Thanks for any help!
namespace SVNQuickOpen.Configuration
{
public class DatabaseRecord : INotifyPropertyChanged
{
public DatabaseRecord()
{
IncludeFolders = new List<string>();
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
private string _name;
public string Name
{
get { return _name; }
set
{
this._name = value;
Notify("Name");
}
}
private List<string> _includeFolders;
public List<string> IncludeFolders
{
get { return _includeFolders; }
set
{
this._includeFolders = value;
Notify("IncludeFolders");
}
}
}
}

You should use ObservableCollection<string> instead of List<string>, because unlike List, ObservableCollection will notify dependents when its contents are changed.
And in your case I'd make _includeFolders readonly - you can always work with one instance of the collection.
public class DatabaseRecord : INotifyPropertyChanged
{
private readonly ObservableCollection<string> _includeFolders;
public ObservableCollection<string> IncludeFolders
{
get { return _includeFolders; }
}
public DatabaseRecord()
{
_includeFolders = new ObservableCollection<string>();
_includeFolders.CollectionChanged += IncludeFolders_CollectionChanged;
}
private void IncludeFolders_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Notify("IncludeFolders");
}
...
}

The easiest way to make WPF's list binding work is to use a collection that implements INotifyCollectionChanged. A simple thing to do here is to replace or adapt your list with an ObservableCollection.
If you use ObservableCollection, then whenever you modify the list, it will raise the CollectionChanged event - an event that will tell the WPF binding to update. Note that if you swap out the actual collection object, you will want to raise the propertychanged event for the actual collection property.

Your List is not going to fire the NotifyPropertyChanged event automatically for you.
WPF controls that expose an ItemsSource property are designed to be bound to an ObservableCollection<T>, which will update automatically when items are added or removed.

You should have a look at ObservableCollection

Related

INotifyPropertyChanged does not work from class held in ObservableCollection<Class>

I have a class, "BaseClass" that implements INotifyPropertyChanged and has the following:
BaseClass:
private bool isOn;
public bool IsOn
{
get { return isOn; }
set
{
isOn = value;
RaisePropertyChanged("BaseClass:IsOn");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
I then have a class, "DIClass" that also implements INotifyPropertyChanged. It also has an ObservableCollection<BaseClass>:
DIClass:
public ObservableCollection<BaseClass> ClassesOfA;
private string iNPCTest;
public string INPCTest
{
get { return iNPCTest; }
set
{
iNPCTest = value;
RaisePropertyChanged("DIClass: INPCTest");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
My ViewModel holds an intance of "DIClass" and registers to it's PropertyChanged event. When I set the value of INPCTest in "DIClass", the ViewModel 'captures' the event correctly. However when I updated the IsOn property within the ObservableCollection, as below, the event is not picked up in the ViewModel.
ClassesOfA[0].IsOn = true;
Why is the INPC interface not working with the nested property? The question and answer here seems quite relevant, but I can't figure it out.
EDIT: additional explanation and code:
I can register to the PropetyChanged events of the ObservableCollection's items, as such:
ClassesOfA[0].PropertyChanged += DIClass_PropertyChanged;
ClassesOfA[1].PropertyChanged += DIClass_PropertyChanged;
However, this still does not bubble up to notify my ViewModel, that a property of my DIClass's ObservableCollection<BaseClass> has changed. I want to use INPC to bubble up event information / property updates up via MVVM layers. But I want to "wrap" them to make my classes cleaner/ less properties lying around
EDIT:
I add this "sketch" of my problem/scenario, with basic naming to make it easy:
To answer your question: This is by design.
ObservableCollection has two events:
CollectionChanged: Fires when the collection changes, e.g. collection.Add( item )
PropertyChanged: Fires when the property changes, e.g. collection = new ObservablecCollection<T>();
I think you need no ObservableCollection, because - as far as I understand your question - you want to observe the changes of the properties of the items in the collection. To achieve that you need to register to each observed item's PropertyChanged like this:
public List<BaseClass> Collection {get;set;}
public void InitializeCollection( IEnumerable<BaseClass> baseClassCollection){
Collection = new List<BaseClass>();
foreach(var item in baseClassCollection){
item.PropertyChanged += MethodToCallOnPropertyChanges;
Collection.Add( item );
}
}
public void MethodToCallOnPropertyChanges(object sender, PropertyChangedEventArgs e){
//react to any property changes
doSomething();
//react to specific properties
if(e != null && e.PropertyName.Equals("isOn"))
doSomethingOtherStuff();
}
This can be very annoying and can causes some other problems.
If I would come across this, I would think about redesigning the ViewModels and the UI. I would try to have an UI which is bound to each BaseClass item. For example, if I have an ListView I would provide an ItemTemplate in which the BaseClass item is bound. Doing so would prevent the need of registering to each item's PropertyChanged.
My suggestion is that you could create a customized ObservableCollection class that raises a Reset action when a property on a list item changes. It enforces all items to implement INotifyPropertyChanged.
I made a simple demo and you that you could check:
public class DIClass : INotifyPropertyChanged
{
public ExObservableCollection<BaseClass> ClassesOfA
... other code...
}
public sealed class ExObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
public ExObservableCollection()
{
CollectionChanged += AllObservableCollectionCollectionChanged;
}
public ExObservableCollection(IEnumerable<T> pItems) : this()
{
foreach (var item in pItems)
{
this.Add(item);
}
}
private void AllObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
Then you could use the ExObservableCollection class in the DIClass object. When the properties inside the BaseClass changes, the UI will be updated.
Update:
Finally, I found out the unexpected behavior you mentioned based on the complex sample. The ExObservableCollection class works well and fires the property changed event correctly.
The key point is you think if the property change event in baseclass is fired then it will
trigger the property change event in DIClass as well, right? I have to say that is not correct. The property change event only fires in the current class. It won't pass to the parent class unless you handle it in the parent class. It fired only once and notify the UI when the target property is changed.
If I understand your scenario correctly, you want to change the ToggleButton's status when the same property in BaseClassobject is changed. But the ToggleButtons are bind to VMData objects so that you need to get notified when the BaseClass objects are changed in the DIClass objects. So you want the the property change event of BaseCasss triggers the property change event of the DIClass.
Handling the property changed event of BaseClass in the DIClass object is the correct way to do what you want. It's the same like handling DIClass event in the ViewModel. But you don't want it since there might be many objects.
Then the first version of your sample is the recommended way to achieve what you want by triggering the property changed event of the DIClass on your own.

Update automatic ListBox items when alter List<T>

I have a ListBox, I populate it with ItemsSource with List<Control>.
But when I delete or add new control for this List, I need every time reset my ListBox ItemsSource
Have any method for ListBox sync List content?
Instead of using a List<T>, use an ObservableCollection<T>. It is a list that supports change notifications for WPF:
// if this isn't readonly, you need to implement INotifyPropertyChanged, and raise
// PropertyChanged when you set the property to a new instance
private readonly ObservableCollection<Control> items =
new ObservableCollection<Control>();
public IList<Control> Items { get { return items; } }
In your Xaml, use something like this...
<ListBox ItemsSource="{Binding MyItemsSource}"/>
And wire it up like this...
public class ViewModel:INotifyPropertyChanged
{
public ObservableCollection<Control> MyItemsSource { get; set; }
public ViewModel()
{
MyItemsSource = new ObservableCollection<Control> {new ListBox(), new TextBox()};
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
This will present the items to the ListBox. In the example here, the collection contains a ListBox and a TextBox. You can add/delete from the collection and get the behaviour you are after. Controls themselves are not all that great as ListBox items because they do not have a meaningful way of populating a visual. So you will probably need to run them through an IValueConverter.
Implement INotifyPropertyChanged interface in your viewmodel.
Post that in the setter of this List, call the NotifyPropertyChanged event. This will
result in updating your changes on UI

how to implement observable int in wpf ViewModel?

In my mvvm ViewModel I have such field
public int Delta { get; private set; }
However when I update it like that:
Delta = newValue;
UI is not refreshed.
I was thinking that databinding will do that for me. For example I can declare collection as ObservableCollection and then databinding will work.
However there are no ObservableInt, how to say View that it need to be refreshed then?
Probably I should raise some event "notify property changed" or something?
You have two choices:
Implement the INotifyPropertyChanged interface on your class.
Inherit from DependencyObject and implement Delta as a DependencyProperty.
The simplest option is #1. You can implement the INotifyPropertyChanged interface on your class quite easily:
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set { _delta = value; PropertyChanged?.Invoke(nameof(Delta)); }
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can read more about using and implementing dependency properties on MSDN.
While we're at it with improving the answer, some of the other new additions of c# 6.0 and 7.0 help make it ever more compact:
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get => _value;
set { _value = value; NotifyPropertyChanged(nameof(Value)); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
This way, you aren't using any "embedded values" (i.e - the property's name) and are keeping the code refactor-safe.
And there's also no need for redundant code blocks due to c# 6.0 and 7.0's new Expression body features
Using #LBushKin's Answer, i modified it to
public class Prop<T> : INotifyPropertyChanged
{
private T _value;
public T Value
{
get { return _value; }
set { _value = value; NotifyPropertyChanged("Value"); }
}
public event PropertyChangedEventHandler PropertyChanged;
internal void NotifyPropertyChanged(String propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
and to set it up:
class MainWindow ...
// a bool with initial value of true
public static Prop<bool> optionBool { get; set; } = new Prop<bool>{ Value = true };
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// connect UI to be able to use the Prop
DataContext = this;
}
and to use it:
<Grid ...
<CheckBox Content="Da Check" ... IsChecked="{Binding optionBool.Value}"/>
There is also a Collection and 2-Properties version here:
Utils.ObservableProperties.cs (this repo contains several related classes)
Just implement INotifyPropertyChanged Interface in your class and use it to raise a PropertyChanged for your Property and then UI will update. If you are using an MVVM project template then there is a good chance you already have a helper method implemented you only need to use it.
MSDN INotifyPropertyChanged
GalaSoft MVVM Light Toolkit
The ObservableCollection raises events automatically but for your own properties you have to raise the events yourself.
A good example is here: http://www.codeproject.com/Tips/228352/Naming-Properties-in-MVVM?display=Print
I'd suggest using mvvm light: http://mvvmlight.codeplex.com, I used it in silverlight and wpf applications. Very easy to use and provides a messageing system between model, view model and view.
Adding on to https://stackoverflow.com/a/8316100/5725669, there is a new and easy way to do this without remembering to keep track of PropertyChanged?.Invoke(nameof(Delta)); in every location
public class YourClass : INotifyPropertyChanged
{
private int _delta;
public int Delta
{
get { return _delta; }
set {
_delta = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged();
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public YourClass()
{
}
// Create the OnPropertyChanged method to raise the event
// The calling member's name will be used as the parameter.
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
It makes use of CallerMemberName for skipping manual entries for property name. More details on this MSDN Doc

Designing WPF classes for DataBinding

I have to build a chat app in WPF; I want to use DataBinding (still learning it) and want to do it in the right way. I've built a Buddy class in this way:
public class Buddy: INotifyPropertyChanged
{
private String _name;
private String _status;
public String Name
{
get
{
return _name;
}
set
{
_name = value;
NotifyPropertyChanged("Name");
}
}
public String Status
{
get
{
return _status;
}
set
{
_status = value;
NotifyPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
I don't know which is the best way to handle BuddyList. Should I create a BuddyList class with Add and List method and then DataBinding to an instance of that class? What is the best way to do it?
You can use INotifyCollectionChanged Interface to create your BuddyListClass
Look here for an example: Usage of INotifyCollectionChanged
Also you can use ObservableCollection<T> Class.
If you want to bind collections of items you should use the
ObservableCollection class
and another suggestions create and base class that implement INotifyPropertyChanged interface,
and derive from it each class that you want to bind to UI.
I think your class definition looks just fine.
Regarding the list question I would expose my list as readonly for binding, and all the add, delete, edit functionality I would keep it private. To avoid informing manually your view for changes to your collection, I would use an ObservableCollection but exposing it to public as ReadOnlyObservableCollection.

How can I detect changes to item properties in the BindingList<T>?

I have a custom class Foo with properties A and B. I want to display it in a databinding control.
I have created a class Foos : BindingList<Foo> .
In order to update some internal properties of the Foos class I need to be notified of property changes (I can handle insertions, removals etc.) on the items in the list. How would you implement that functionality ?
Should I inherit Foo from some object in the framework that supports that ? I think I could create events that notify me if changes, but is that the way it should be done ? Or is there some pattern in the framework, that would help me ?
Foo should implement the INotifyPropertyChanged and INotifyPropertyChanging interfaces.
public void Foo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private int _someValue;
public int SomeValue
{
get { return _someValue; }
set { _someValue = value; NotifyPropertyChanged("SomeValue"); }
}
}
The BindingList should hook onto your event handler automatically, and your GUI should now update whenever you set your class invokes the PropertyChanged event handler.
[Edit to add:] Additionally, the BindingList class expose two events which notify you when the collection has been added to or modified:
public void DoSomething()
{
BindingList<Foo> foos = getBindingList();
foos.ListChanged += HandleFooChanged;
}
void HandleFooChanged(object sender, ListChangedEventArgs e)
{
MessageBox.Show(e.ListChangedType.ToString());
}

Categories