What is the best way to implement collection of items, where:
each item could be of different type
each item will raise PropertyChanged event on its change (parent class implements INotifyPropertyChanged)
Update:
I'm thinking on something like this:
Collection:
["Name", string:"John Doe"]
["Age", int:"32"]
["Profiles", List<Profiles>:"list of profiles"]
I will be able to add new item like this:
Collection.Add("NewItem", value);
And then change it:
Collection["NewItem"] = newValue;
Which will trigger event:
NotifyPropertyChanged("NewItem");
So, as a result, I will be able to subscribe to every-single item from that collection and have different event handlers.
Thanks.
What you need from your description is what I (and others) would call an ObservableDictionary, which Google will give you several suggestions for implementing. There's no such class built in to .NET, but there are tuturials and third-party implementations a-plenty.
The reasons I say this:
the correct interface for notifying that the content of a collection has changed is INotifyCollectionChanged, not INotifyPropertyChanged. When you add an item or call Collection["NewItem"] = newValue, the event you should fire is CollectionChanged, because it's not a property of the list that has changed - it's the content
you're clearly intending for your list to have key/value pairs, which means you can't easily use the existing ObservableCollection class.
Separately, you say "I will be able to subscribe to every-single item from that collection," which suggests that you may want to limit the "values" in your dictionary to be INotifyPropertyChanged but, I would suggest, you should create your ObservableDictionary as a generic class, and then you can choose to have the values in the dictionary be any type you need.
If it has to be a list, use
ObservableCollection<INotifyPropertyChanged>
Related
This is very similar to the question but specific to Lists.
How to trigger event when a variable's value is changed?
Every time my list is modified I want to run a method.
I assumed using a property and putting a method within 'set' would be the correct way to approach this. I started with something like this
public class Stuff
{
private List<Things> _myThings;
public List<Things> MyThings
{
get
{
return _myThings;
}
private set
{
_myThings= value;
runWhenListIsChanged();
}
}
}
However, when I use a command like 'RemoveAt' or 'RemoveRange'
public class StuffChanger
{
Stuff.MyThings.RemoveAt(5);
}
It goes straight into the 'get' property and the list is changed.
I wrongfully presumed it would use set (as its changing the list) but that's not the case. When debugging (using the 'step into' tool in Visual Studio) I noticed the List can be modified by using the 'get' accessor.
In this particular case, I don't want to have a method being called every time the list is read by something, as this could get performance heavy or cause a stack overflow. So that isn't an option.
I'd be very grateful if anyone has any tips or code suggestions! Thanks very much.
As you have observed, the code you wrote to manipulate the list does in fact not set a new list into the property.
Instead, this code:
Stuff.MyThings.RemoveAt(5);
can be thought of as similar to this:
var list = Stuff.MyThings;
list.RemoveAt(5);
As such, it reads the list out of the getter, and then directly manipulates the list itself.
To get notifications when the list itself is changing, you will need to use something other than List<T> since this type does not have any functionality to support that requirement.
A good candidate would be ObservableCollection<T> and you would hook up an event handler to the CollectionChanged event.
Please note that this will only observe changes to the list, and not to the elements in the list. If you add or remove an item from the list, you will get notifications but if you execute code like this:
MyStuff.MyThings[0].SomeProperty = SomeNewValue;
then you will not get any notifications from the list itself. You will have to implement something like INotifyPropertyChanged on the element types, and hook up event handlers to that to get those notifications.
I am new to the MVVM-pattern, and am trying out Caliburn.Micro on a project.
I want to have one ViewModel (which contains a collection of ViewModels) shared by multiple Views, where each view only displays items which have a certain value on one of it's properties.
To get specific, I am using a service which allows me to monitor different values that update frequently. I then get an object of type MonitoredItem, which contains a property of type DataValue, which in turn contains an object for the Value and a property for the value's datatype.
So far I have a MonitoredItemViewModel which uses this service's MonitoredItem class as it's model, and a MonitoredItemsViewModel which contains BindableCollection<MonitoredItemViewModel> MonitoredItems, and commands for adding/removing items.
I also have a MonitoredItemsView where I can see all the items I am currently monitoring.
How do I go about splitting up the view, so that I can have all MonitoredItems where DataValue is an integer/float/double displayed in one area in my window, boolean values displayed somewhere else etc?
Don't do it in the view, instead expose different collections on your ViewModels according to what you need to filter.
This can be done either as known collections, e.g.
public ObservableCollection<MonitoredItemViewModel> ItemsWhereFooIsBar ...
public ObservableCollection<MonitoredItemViewModel> ItemsWhereFooIsntBar ...
or you could do it more generically to return filtered collections on demand
public ObservableCollection<MonitoredItemViewModel> GetItems(Func<DataValue, bool> matches)
{
//Filter collection with
return ... allItems.Where(x=>matches(x))... ;
}
and call via
GetItems(x=>x.Foo == Bar)
The problem you are going to have is when the items change and should switch from collection to collection. If you were using ReactiveUI this would be incredibly easy as you can use Rx to trigger its built in item tracking and also use its .CreateDerivedCollection(...) to build the new collections automatically (hint, hint :-))
If not then you have a few choices.
You can derive a class from ObservableCollection so that as well as being notified via CollectionChanged when new items are added or removed, or also get notified when the properties of the items change as well.
Or you could make your ItemViewModel immutable so that its properties never change, but instead you drop the old item and add an updated one into the correct collection.
I have a class that uses INotifyPropertyChanged and have a List bound to a grid. Every time that object changes it updates the values in the grid. However for historical reasons I want to add that object to a collection/list and bind to a history grid to show all the changes. However every list I have tried seems to subscribe to INotifyPropertyChanged so I only ever get the same amount of items in my history grid.
Is there a list/collection that doesn't subscribe to INotifyPropertyChanged?
Thanks
If your List is a list of references to a class, you will need to do a "deep clone" of the entire list in order to maintain a historical copy. Otherwise, a copy of the list's contents will still be references to the "live" objects which are getting changed. Doing this will require code such as:
// This requires a way to "Clone" your object...
List<YourClass> listCopy = originalList.Select(item => item.Clone()).ToList();
If the list contains value types (struct), you can just create a new List<YourType> and copy the original elements over.
// If your type is a value type, you can just copy the list directly...
var listCopy = originalList.ToList();
Also - this has nothing to do with INotifyPropertyChanged. Lists, themselves, do nothing with the PropertyChanged event.
Consider using Mode=OneTime in your binding. http://msdn.microsoft.com/en-us/magazine/cc163299.aspx#S3
(update) ICustomTypeDescriptor works for my Windows Forms app, but not for Silverlight; Not supported. I will keep investigating this idea though and see where i get to.
(/update)
I have, say a few switch panels (for those that like analogies).
Each of these switch panels has switches that have a Name(string) can be in state(bool) of On or Off.
The switchpanel and switches are objects that have INotify interface on them.
Using the switches Names, I create a list of all possible switch names over the collection and create a dynamic class that has all these Names as properties.
SwitchPanel1 (Switches( Switch1 ("Main",On) , Switch2("Slave",Off)))
SwitchPanel2 (Switches( Switch1 ("Bilge",On) , Switch2("Main",Off)))
Produces a collection of
(Main,Bilge,Slave)
And a dynamic class is produced that has the properties:
SwitchPanel : (SwitchPanel)
Main : (Switch)
Bilge : (Switch)
Slave: (Switch)
The idea is that if the switch panel has a switch with the Name of the property, it is placed on that property. So using a bit of linq
propeties["Main"].SetValue(newSwitchType,SwitchPanel.Switches.FirstOrDefault(sw => sw.Name == "Main"));
I want to cast this new dynamic class to INotfyPropertyChanged AND catch the actual changes on these new properties, so if a switch changes state the dynamic object will report it.
Why? It needs to be displayed in a list view and the list view I'm using has its binding by supplying the Property name, and not the binding path.
It also attempts to catch INotify events by casting the object against INotifyPropertyChanged. This means it will sort and/or group when things change.
If you know of a better way to do this let me know. Please.
You probably don't need a dynamic class. You can implement runtime binding properties via ICustomTypeDescriptor / GetProperties(), creating your own PropertyDescriptor implementation that returns the named switch. It isn't clear what knows first about the change, but you could either use INotifyPropertyChanged, or the older property-specific change event, again tied to each property (so each PropertyDescriptor attaches to, for example, the event in the named switch.
Not trivial, but not impossible either.
I've got a data object with a component in it that is an System.Collections.Generic.IList, and I'd like to reflect changes to that list into a Gtk# NodeView, so that when an item is added to the list, the NodeView will get a new item added to it.
How would I listen for changes to an IList? I have considered wrapping the IList with a class that implements IList, delegates the requisite methods, and broadcasts an event when changing it's contents, but that seems like a lot of work for something that has probably already been solved by someone else.
Gtk.DataBindings is wahat you're looking for.
Do System.Componen.BindingList or System.Collections.ObjectModel.ObservableCollection exist in mono?