Run a method everytime a List is modified (not accessed) - c#

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.

Related

Create a copy of an ObservableCollection that doesn't update

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.

What is the correct way to create methods that change private fields in C#?

I just switched to C# from Java and just became familiar with the notion of Property, which seems to be the common way of getting and setting field values.
So what should I do if I need to update the field values pretty often, but not setting them to totally new values? Like, there is a field in my class that is a List, and I need to sometimes append elements into it, while keeping the rest of the List unchanged. Should I just go ahead and create a method like
void append(Point p) { }
or is there a more elegant or civilized way of doing this in C#?
There is no single "correct" way of setting private fields through an API. The answer depends on what functionality you would like to present to your users.
If you would like to let them access your List<Point> as a read-only collection which they can modify in any way they like, you may present your list as a read-only property:
public IList<Point> Points {get;} = new List<Point>();
If you think that this approach gives your users too much freedom, and you would prefer to have tighter control over the points that appear on the list, you may want to expose a property for accessing the list as IEnumerable<Point> and a bunch of methods for adding / removing / modifying points on the list.
private IList<Point> points = new List<Point>();
public IEnumerable<Point> Points => points;
public void AddPoint(Point p) {
// validate p before inserting on the list,
...
points.Add(p);
}
Note: Code examples above use C# 6 syntax.
If you wanted to allow modification to the list but not allow the instance to be changed you could write
public IList<Point> MyPointList {get; private set;}
However if you wanted to only support adding elements, then the add method would be a good way to go.
C# syntax is simple and elegant and a joy to write with. In a Class you can create a private field that backs up a public getter and setter. From within this Class, you can set or get the private field as you like. If a different Class needed to access the field, it would need to use the public getter and setter. Example: A private List could be created and edited from within the class however you choose to. With a custom add method or using the List .Add method. From a different class you would simply call the public List properties .Add method. There really is just one private List that is being edited. You can create another private List for another task. Maybe a staging area for items waiting to be validated before entering the main list.

Update Single Item in the ObservableCollection without LINQ

I am trying to create a list of the Components running on the network. I am trying to get all the components in the ObservableCollection. ObservableCollection<ClsComponent> Now my question is if one of the component in the collection get changed / modified how would I be able to get it reflected to my ObservableCollection of Component
Is there a way to change the it directly in the collection itself?
What is the fast and efficient way doing it?
I have tried: to change it using the LINQ : Find the Component in the collection and change it?
var CompFound = Components.FirstOrDefault(x=>x.Id == myId);
Components.Remove(CompFound);
Components.Add(UpdatedComp);
I am very sure there should have been more optimized way of doing this. Please suggest.
Edit
I am trying to write the code in the function where I can get the parameters of Source Component and Destination Component. Function looks like this
public void UpdateComponent(ClsComponent SourceComp, ClsComponent DestComp)
{
//do something here
}
After the execution of the function I want to Replace Source Component with Destination Component.
I believe this might work for you. I am sure you might be looking for this
Components.Insert(Components.IndexOf(SourceComp), DestComp);
Components.Remove(SourceComp);
One of the most efficient way would be to use a dictionary. There are implementations of ObservableDictionary which will give you the Observable behavior while allowing a fast key-based access to the object.
Check this stackoverflow question. It includes Microsoft's
It should work like ObservableCollection, except it's also a dictionary. So you can Create ObservableDictionary<Int,ClsComponent>
To replace the value simply call
Components[myId] = destComp

ComboBox not updating when object added to bound list

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).

List of items implementing INotifyPropertyChanged

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>

Categories

Resources