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
Related
I would like to back up the observable collection used by a WPF datagrid for the purposes of a rollback/undo button to the point of the last commit. I have a refresh option that will read from the database but that will include changes from other sources. There are other similar instances of this question around, but nothing that stood out as a solution.
I have tried copying the observable collection to a list, but any changes made to the OC are also persisted to that list. But aside from a single simple assignment statement that list is not in any other way connected to the observable collection.
Its almost as if the address of the observable collection was assigned to be the address of the backup list in the manner of how it is behaving. How do I overcome this?
Converting an ObservableCollection<T> to List<T> simply changes the type of collection that holds your items. The elements inside the collection remain unchanged. What you're looking to do is cloning your list. To do this your element class will have to implement the ICloneable interface. See how to implement the IClonable interface here. Once that's in place, all you have to do is call:
var clone = myObservableCollection.Select(i => (MyType)i.Clone()).ToList();
I am building a control in xamarin forms that binds to a list of objects. To get this binding to work I need to use observable collections (otherwise propertychanged methods don't fire).
I noticed a really frustrating interaction as a result of needing to use OC's as opposed to lists. Whenever the binded OC updates, the values in my controls are automatically updated, even if they are just references of the OC, Here is how i am copying the OC.
//Internal list of events
private List<EventItem> _events;
void OnEventsChanged(ObservableCollection<EventItem> eventsCollection)
{
//Error handle
List<EventItem> events = eventsCollection.ToList();
//Do something
_events = events;
}
The problem comes when the OC updates, I want to check for new/deleted AND altered objects. The issue is that when the OC updates, it is updating the internal list (_events) aswell. This means when I go to do comparisons between the old & new values, they are the same.
Honestly I don't really understand how c# handles copying references of objects around, I had a similar issue a while back with DateTime.Now being calculated as opposed to copying the value of the already initialised object.
var time = DateTime.Now;
await Task.Delay(1000);
var time2 = time; //This is 1 second later than time, not the value of time (which is what I wanted)
I have used Objective-C in the past and that has the concept of MutableCopy where you can assign a new list from an existing one, they have the same values but aren't linked.
How can I do this in C# so that my controls internal list is only updated by me and not the OC?
Thanks
That's perfectly normal. If I have enough time, I'll try to explain it to you.
In a nutshell, the observableList (or a List) is a list of reference to the objects and not a list of objects. The thing is that the objects are not copied inside a list but the list contains a reference to the different objects. That means that if you do something like ToList(), you get another list of references to the exact same objects.
Now to solve your problem. Just create a new list with new objects with something like
var newList = oldList.Select(x => new Model(x)).ToList();
And of course the Model class has a constructor that accept a Model as a parameter and copy the properties.
When you write _events = events;, you create not a new object, but a reference for the same object. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/index .
You should to clone (create a copy of object itself) as it mentioned in comment by #Matt.
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 an object that represents a client, and that object has a list of the clients branches:
private List<Branch> _branches;
[System.Xml.Serialization.XmlArray("Branches"), System.Xml.Serialization.XmlArrayItem(typeof(Branch))]
public List<Branch> Branches
{
get { return _branches; }
set
{
_branches = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Branches"));
}
}
}
In one form (WinForms) I have a ComboBox that I've bound to that list:
// creating a binding list for the branches
var bindingList = new BindingList<Branch>(Client.Branches);
// bind list to combo box
cmbBranches.DataSource = bindingList;
cmbBranches.DisplayMember = "Name";
cmbBranches.ValueMember = "Name";
In another function, I create a new Branch object and add it to the existing list: Client.Branches.Add(newBranch). I would expect this to update the ComboBox but it doesn't. Why not, and how do I make it update? (Edit: I'd also like this to update when removing an object from the list. The reason it doesn't work is, I assume, directly related to why the box isn't updating when Add is called.)
In doing research, I found this SO answer, which seems to imply that it will work. I feel like I'm missing something simple ...
difference between ObservableCollection and BindingList
Edit: Some further information about what I've tried and some additional goals.
I cannot use ObservableCollection<T> instead of List<T> as I need to use Exists in the code. Which the former doesn't have.
I need to update the original list when the new object is added, in addition to updating the drop down box.
To summarize my comments below, I attempted adding this:
var bindingList = (BindingList<Branch>) cmbBranches.DataSource;
bindingList.Add(frmAddBranch.NewBranch);
But that results in the object being added to the ComboBox twice. Somehow by calling bindingList.Add it's "resetting" the data source and doubling up. I cannot find any function that "refreshes" the data display once it's bound. Control.ResetBindings() did not work.
Well, it doesn't work that way. The inner List<T> has no change notification mechanism, so adding directly to inner List<T> will not generate any change notification that would eventually reach the combo box. Most convenient way to do what you want is adding the item through the BindingList<T> instead.
I believe you have to add the items directly to the BindingList (but not to the backing Branches list - the BindingList should take care of this for you).
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>