How to subscribe to events fired from any collection element in C#? - c#

I have made the following Dictionary, and I would like to be able to subscribe to the event fired from any of its' elements, in order to know which dictionary elements' properties were changed.
Here is my class:
public class BlockInput : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
private int _value;
public int Value
{
get
{
return _value;
}
set
{
_value = Value;
NotifyPropertyChanged("Value");
}
}
}
I create a concurrent dictionary like the following:
public ConcurrentDictionary<string, BlockInput> Inputs;
How would this be achieved, in order for me to find every time that one of the BlockInput values were changed/event for each element fired?
Thanks for your time.

I don't believe you have another way than manually subscribing to all events yourself:
foreach (BlockInput item in Inputs.Values) {
item.PropertyChanged += BlockInput_PropertyChanged;
}
private void BlockInput_PropertyChanged(object sender, PropertyChangedEventArgs e) {
var blockInput = sender as BlockInput; // Get the item that was changed
// Do stuff
}
You would have to subscribe to all added items and unsubscribe from removed ones as well if you plan to add or remove items from the Dictionary.

In practical use of this Interface (wich is primarily MVVM) usually this is enough. You have the GUI classes do all the plumbing work of subscribing to events. You only need to provide 3 Change Notifications:
The one of each property of BlockInput. You did that in your example code.
The one if something is added or removed from the Collection. That is what ObservableCollection<BlockInput> will take care off. It is also the only thing the OC will take care off.
The one on the property exposing the ObservableCollection<BlockInput>. The OC is notoriously bad at bulk modifications, so often you need to prepare a new instance in code, with Exposing being the last step.
If you do not have a MVVM use case, please leave a comment. I can think of 2 ways on top of manually subscribing to each Event.

Related

ObservableCollection<T> isn't getting notified of change in property of <T>

I have a an ObservableCollection of Component, a class with another ObservableCollection, a String and a ComponentVersion. The SelectedComponentVersion is being updated via my view correctly but I'm unable to get the property of Components to fire it's setter/OnPropertyChanged.
private ObservableCollection<Component> components
public ObservableCollection<Component> Components
{
get { return foo; }
set { foo = value; OnPropertyChanged(); }
}
Here is the class of Component.
public class Component : ViewModelBase
{
private string componentName;
private ObservableCollection<ComponentVersion> componentVersions;
private ComponentVersion selectedComponent;
public string ComponentName
{
get { return componentName; }
set { componentName = value; OnPropertyChanged(); }
}
public ObservableCollection<ComponentVersion> ComponentVersions
{
get { return componentVersions; }
set { componentVersions = value; OnPropertyChanged(); }
}
public ComponentVersion SelectedComponent
{
get { return selectedComponent; }
set { selectedComponent = value; OnPropertyChanged(); }
}
public Component(string componentName, List<ComponentVersion> componentVersion)
{
ComponentName = componentName;
ComponentVersions = componentVersion.ToObservableCollection();
}
}
I then have a binding from a listview onto the SelectedComponent property inside of Component.
I have read countless stack overflows about setting up CollectionChanged and have tried to implement it with no luck.
Components.CollectionChanged += stuff;
private void stuff(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
throw new System.NotImplementedException();
}
but this is never hit as tested with breakpoints.
Am I missing something entirely, losing my mind or daft! Someone please give me a point in the right direction, if any of this makes any sense at all.
P.S another solution I though of would be to place an invisible button inside the listview and have that send a command to tell the vm that a selected item has been updated.
In cases like this where I want to do something when the property on an item inside the collection changes, I usually hook up a PropertyChanged event in the CollectionChanged event
Here's a code example :
public MyViewModel()
{
// Setup Collection, with a CollectionChanged event
Components = new ObservableCollection<Component>();
Components.CollectionChanged += Components_CollectionChanged;
}
// In the CollectionChanged event (items getting added or removed from collection),
// hook up the PropertyChanged event
void Components_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
foreach(MyType item in e.NewItems)
item.PropertyChanged += Component_PropertyChanged;
if (e.OldItems != null)
foreach(MyType item in e.OldItems)
item.PropertyChanged -= Component_PropertyChanged;
}
// In the PropertyChanged event, run some code if SelectedComponent property changed
void Component_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "SelectedComponent")
DoWork();
}
Note that we are using two separate events here.
CollectionChanged event runs when the collection itself changes. This means it gets set to a new collection, or item(s) get added or removed from the collection.
This captures newly added items and hooks up the PropertyChanged
handler, or detaches the PropertyChanged handler for items being
removed from the collection.
PropertyChanged event runs when a property changes and fires the event. You'll use this event to run code when the SelectedComponent property on any item in the collection changes
Well, components hasn't changed. I'm not sure if you set up your handler correctly, but even if you did, a CollectionChanged event is only fired, if the collection changed (item added or removed).
Lets say you have a collection of cars and all are red.
You pick one car and set it's color to blue.
The collection has not changed. It's still the very same cars. No car is missing, no car was added.
You probably want to attach yourself to the handlers of all cars instead of the handler of the collection.
So to sum it up:
ObservableCollection<T> isn't getting notified of change in property of <T>
That's true and it's by design.
When adding items to the collection you want to "hook up" the events you are interested in for those items, then you can act as needed. As mentioned above, the collection isn't changing it is the individual items that are changing.
One thing you could do is extend the ObservableCollection class and override the functionality so that when the collection is changed, an item is either added or removed, you then go through the items in the collection and "hook up" the events you're interested in. One thing to note is you may have to go through the collection and remove the event handlers and "hook up" them again in order to stop getting multiple event handlers being set for the items in the list.
ObservableCollection<T> does not send notifications about property changes of the elements. However, the System.ComponentModel namespace contains another collection, which supports that: you can consider to use BindingList<T> instead, which is also supported by WPF (elements must implement INotifyPropertyChanged).
Please note though that BindingList<T> scales poorly and its performance starts to decline above hundreds and thousands of elements as it always searches for the changed element sequentially in order to return the element index in its ListChanged event.

Custom Event in Custom Control (on variable changed)

I'm making a custom control including three buttons. Every button represents a tab. When I click a button to change to the tab corresponding to that tab a variable for the controll is uppdated, this variable is called "selectedIndex". How can I make a custom event that triggers when this index is changed?
I've seen solutions for custom events here but all of them is copies of already existing events such as Click-events. My problem involvs having an event on variable change".
Regards!
One option is to have your object implement the very standard INotifyPropertyChanged interface. There's no rule which states that every property on an object needs to raise the PropertyChanged event, so just raise it for the one property in question. Something like this:
public class MyObject : INotifyPropertyChanged
{
private string _myProperty;
public string MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
If your .NET version doesn't support CallerMemberName then you can remove that and supply the property name manually:
NotifyPropertyChanged("MyProperty");
At this point any calling code can subscribe to the public PropertyChanged event on any instance of this object. This interface is particularly handy because it's commonly used in lots of UI technologies for updating data-bound UI elements. All the interface enforces, really, is a standard name for the event. If you'd prefer, you can forgo the interface and just create a custom public event. And just invoke that event any time your object logically needs to. The structure of exposing an event and invoking it would be the same.

how to provide change notification for a property when a subproperty changes?

This is such a basic question, but I don't think I've done this before despite having bound so many properties. I originally was planning to bind a class called TimeScale to various objects.
In class A we have a dependency property that I want to call change notification on. However, change notification is not done manually through this class.
public TimeScale AxisTimeScale
{
get { return (TimeScale)GetValue(AxisTimeScaleProperty); }
set { SetValue(AxisTimeScaleProperty, value); }
}
public static readonly DependencyProperty AxisTimeScaleProperty =
DependencyProperty.Register("AxisTimeScale",
typeof(TimeScale), typeof(SignalPanel),
new FrameworkPropertyMetadata(new TimeScale()));
this binds to source class B
private class B
{
private TimeScale _GraphTimeScale;
public TimeScale GraphTimeScale
{
get { return _GraphTimeScale; }
set
{
if (value != _GraphTimeScale)
{
_GraphTimeScale = value;
OnPropertyChanged("GraphTimeScale");
}
}
}
}
Looking at it again I guess all I really want is to call propertychanged on a dependency property, but since I didn't implement Inotifypropertychanged, I am wondering how i do that.
I think DependencyObject already implements Inotifypropertychanged, so I have access to this:
OnPropertyChanged(new DependencyPropertyChangedEventArgs(property, old value, new value));
However, inserting the same object into both the old value and new value slots results in the PropertyChanged event not firing (I assume the implementation checks whether the two values are the same before firing the event). I want to avoid creating a new object if possible. I guess one option is to override OnPropertyChanged. Nope that also requires me to have a dependency propertychanged event args.
Update
OnPropertyChanged("TimeScale");
to
OnPropertyChanged("GraphTimeScale");
Or,
you can wrap the TimeScale class with an ObservableObject so that you can subscribe to object change events and raise them from there.
More info: http://msdn.microsoft.com/en-us/library/ff653818.aspx
Subscribe to the PropertyChanged notification of NumberOfUnits, and then raise OnPropertyChanged("GraphTimeScale") in the property changed event handler.
Would be interested if there is a better way though.

INotifyPropertyChange ~ PropertyChanged not firing when property is a collection and a new item is added to the collection

I have a class that implements the INotifyPropertyChanged interface. Some of the properties of the class are of type List. For example:
public List<string> Answers
{
get { return _answers; }
set
{
_answers = value;
onPropertyChanged("Answers")
}
}
...
private void onPropertyChanged(string propertyName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
If I assign a new List<string> to Answer, then the PropertyChanged event fires as expected; but if I add a string string to the Answer list using the List Add method, then PropertyChanged event doesn't fire.
I was considering adding an AddAnswer() method to my class, which would handle calling the lists's Add method and would call onPropertyChanged() from there, but is that the right way to do it? Is there a more elegant way of doing it?
Cheers,
KT
You should expose an ObservableCollection<string>, which implements the INotifyCollectionChange interface to raise its own change events.
You should also remove the property setter; Collection properties should be read only.
You should not raise the PropertyChanged event when the contents of the collection change.
Ok I just finally experienced this issue, and there are NO complete answers on the Internet afaikt, so here is the missing piece that no one mentions (maybe because the assume that we are not complete morons and have NOT deleted the default constructor, or have alteast extended the default constructor) anyhow:
Make certain that you DID NOT delete the InitializeComponent(); call in the constructor of your View.
Without this call BEFORE you set DataContext of the view, NotifyPropertyChanged Event will ALWAYS BE NULL. I spent about 2 hours trying to figure out what was different between two different MVVM userControls. I guess my mind is so used to seeing InitializeComponent(); that it did not register that it was missing. I added that back and viola!
Hope This Helps Other Dummies Like Me!
Cheers,
Code Warrior Malo
It's not firing because the collection reference isn't changing, just the contents. You'd need the collection's Add() method to fire the event to be able to see it.
have a look at System.Collections.ObjectModel.ObservableCollection. http://msdn.microsoft.com/en-us/library/ms668604.aspx
It can be used like a List but has events built in for when its contents change.
ObservableCollection is your answer. If you want to fire on collection property changes, you'll need to implement INotifyPropertyChanged for each of the properties you'd like to track.
You should add an event listener to your _answers collection. Unfortunately, List<T> doesn't provide such an event.
As a suggestion, manage _answers as an ObservableCollection<string>, and properly attach/detach an event handler for the CollectionChanged event, as follows:
public ObservableCollection<string> Answers
{
get { return _answers; }
set
{
// Detach the event handler from current instance, if any:
if (_answers != null)
{
_answers.CollectionChanged -= _answers_CollectionChanged;
}
_answers = value;
// Attatach the event handler to the new instance, if any:
if (_answers != null)
{
_answers.CollectionChanged += _answers_CollectionChanged;
}
// Notify that the 'Answers' property has changed:
onPropertyChanged("Answers");
}
}
private void _answers_CollectionChanged(object sender,
NotifyCollectionChangedEventArgs e)
{
onPropertyChanged("Answers");
}

C# custom event handlers

If I have a property:
public list<String> names { get; set; }
How can I generate and handle a custom Event for arguments sake called 'onNamesChanged' whenever a name gets added to the list?
A BindingList is likely your best option as it has builtin change tracking and a variety of existing events you can use. Below is an example of exposing a custom event for Add which forwards to the BindingList event.
class Example
{
private BindingList<string> m_names = new BindingList<string>();
public IEnumerable<string> Names { get { return m_names; } }
public event AddingNewEventHandler NamesAdded
{
add { m_names.AddingNew += value; }
remove { m_names.AddingNew -= value; }
}
public void Add(string name)
{
m_names.Add(name);
}
}
You should check out the System.ComponentModel.BindingList, specifically the ListChanged event.
One alternative to BindingList is ObservableCollection - in this case you'd want to subscribe your own event handler to the CollectionChanged event and fire your event depending on the action.
David Mohundro shows one approach; one other option is to inherit from Collection<T> and override the various methods:
class Foo {}
class FooCollection : Collection<Foo>
{
protected override void InsertItem(int index, Foo item)
{
// your code...
base.InsertItem(index, item);
}
protected override void SetItem(int index, Foo item)
{
// your code...
base.SetItem(index, item);
}
// etc
}
Finally, you could create your own list (IList, IList<T>) from first principles - lots of work, little benefit.
A non-orthodox approach might be using an AOP framework such as PostSharp to "weave" a handler before/after the accessor is called, which fires an event.
You create an external class which contains the pre and/or post handling code for when your property is accessed, check if the value of the property changed between pre and post, and raise an event.
Bear in mind that while taking the value for comparison (inside your handler code), you might get into an infinite loop (you call the property accessor, which calls the AOP handler, which calls the accessor and so on), so you might need to reflect into the class containing this property to attain the backing field.

Categories

Resources