InvalidOperationException while sorting an ObservableCollection - c#

In my program I have TreeView nodes that I need to be able to shift up and down, basically change the order. My TreeView is an ObservableCollection of a specific Data Model. Each node has a property called "Rank", this is the value that I would like to sort the collection by. With that being said I referred to this question. From that question I discovered this blog page. I am attempting the second method involving the sort function of a List.
This is the example that I am looking at:
List<Person> list = new List<Person>(people);
list.Sort();
Please note that the "Rank" value of each node is in working order and changing correctly. I just need to find a way to re-order the collection based off of that property and reflect it in the View.
My Problem: When trying to implement the above solution I get an InvalidOperationException. I feel like I do not understand how to tell the List to sort based off of rank.
What my code looks like:
List<TreeModel> sortedTree = new List<TreeModel>(TreeCollection);
sortedTree.Sort();
What am I missing here? How do I sort the collection based off of the rank property and reflect those changes in the view?
Thank you.
*I believe I may have posted about this before, so if for some reason this question is too similar my older one, I'll just delete the old one.

Sort throws InvalidOperationException its component type does not have a default comparison:
The default comparer Comparer.Default cannot find an implementation of the IComparable generic interface or the IComparable interface for type T.
You can however supply the comparison as the first parameter to Sort:
sortedTree.Sort((x, y) => x.Rank.CompareTo(y.Rank));
To pass the sorted items back to the original collection, you could either clear/repopulate CurrentCollection, or simply assign it a new instance (don't forget to RaisePropertyChanged if you do the latter):
CurrentCollection = new ObservableCollection<TreeModel>(sortedTree);

You need to pass property name on which you want to sort your list like this -
sortedTree = sortedTree.OrderBy(m => m.Rank).ToList();

Related

How to Remove Specific Item from each Object in Generic List

Background
I have a list of generic objects. Each generic object has a specific field I need to remove.
I create the list like this.
list = new List<Object>();
list = JsonConvert.DeserializeObject<List<T>>(OutJson, new BooleanJsonConverter());
However i need to then remove a item from every object in that list. However i don't know how many or what objects are in the list. I do know that there will always be a field that i need to remove however.
Pseudocode
I think I need to do something like this, but in a generic way.
//Loop through list objects, and for each object, loop through its
//properties. If any of the properties match a string, remove
//that property from the object.
foreach (var object in list)
{
foreach (var item in object)
{
if(item.ToUpper() == "SpecificKey")
{
list.Remove(item);
}
}
}
Question
How do I loop through the generic object in a list and remove a specific item if it is present?
I think to complete this, it's better to mark the objects as dynamic.
Then this would work.
List<dynamic> list = JsonConvert.DeserializeObject<List<T>>(OutJson, new BooleanJsonConverter());
list.RemoveAll(x=>x["SpecificKey"]!=null);
I have a list of generic objects. Each generic object has a specific field I need to remove.
If you're trying to access a specific property on an objection, you can use reflection. See the answer here:
Get property value from string using reflection in C#
i need to then remove a item from every object in that list. However i don't know how many or what objects are in the list. I do know that there will always be a field that i need to remove however.
Your pseudocode is code is confusing, but it seems like you're just trying to remove a child from a parent object. If that is the case, and the child is a complex object, then set it equal to null.
If you mean to say that you need to any object from the list that contains a property that has the specified property name, then you should know that you cannot remove items from a list while iterating through it using foreach. See the answer here:
C# List - Removing items while looping / iterating

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

Set property in treeview.selecteditem

I have a WPF treeview that displays multiple nested classes based on hierarchical datatemplates. It uses the classes here: https://complexdatatemplates.codeplex.com/. This question applies to DataGrids also, though or any control who's selected item is an anonymous class.
What I want to do seems simple. I want to select an item, then press a button and operate directly on the selected item--not the SelectedItem property of the tree view, but the object behind it. But, since the compiler doesn't know the class of the selected item until runtime, it understandably won't give me access to any of the methods or properties in it.
The class in this case is 'Roms', the treeview is 'DB_tree'. I can create a new object from the selected item, and cast it as Roms, then do whatever I want. Like this
roms = (Roms)DB_tree.SelectedItem;
But I can't figure out how to operate on the actual object that would be returned by DB_tree.SelectedItem.
Well, it turns out that by assigning (Roms)DB_tree.SelectedItem to another object
DummyObject = (Roms)DB_tree.SelectedItem
Then everything I do to DummyObject is done to the original object. The equals operator for Objects acts more like assigning an alias than an equals operator.
Strange, that, though clearly common sense to OO programmers everywhere. If the equals operator worked the same way for doubles or ints it would be impossible to do math, now that I understand it, it is cleaning up a lot of my code.

Difficulty in Removing Items From List?

I have two lists. The first is of all students and the second is of selected students. I want if I one time select some student they will remove from the all-student list. Here is my code but it doesn't. Students won't get removed.
foreach (var li in ListSelectedStudents.ToList())
{
if (ListAllStudents.Contains(li))
{
ListAllStudents.Remove(li);
}
}
Contains will use equality to determine what is "equal", I am assuming here that your custom class hasn't provided a custom equality implementation, which means the default equatable will be provided for that type and it's just using reference equality. So even though you think two things are "equal", the Contains method doesn't and so doesn't step into the Remove call.
To get that particular code to behave what you need to do is provide an implementation of IEquatable<Student> on the Student class, as described in the remarks here.
In this instance, Contains isn't actually required as Remove will do the same checks. If there is nothing to remove, the Remove call will be transparent, effectively doing nothing.
As has been caught in the comments before I had chance to provide the information, Remove will also rely on IEquatable<Student> (docs) so you still need to provide an implementation, but it will make your code look a little cleaner:
foreach (var li in ListSelectedStudents.ToList())
{
ListAllStudents.Remove(li);
}
There may be various ways to do this without the need to implement the interface, but you won't be able to use your current code for it. I'll leave other answers to field those alternatives as it's Friday and my brain is not yet functioning properly.
have you tried using linq:
ListAllStudents.RemoveAll(m => ListSelectedStudents.Contains(m));
if it does not work, it could be something wrong with the default comparison implemented in the object, and you could either fix the comparer, or do something like:
ListAllStudents.RemoveAll(m => ListSelectedStudents.Any(n=>n.Id == m.Id)); // Assume the Id is the primary key of the object...
Try this:
ListSelectedStudents = ListSelectedStudents.Where(a => !ListSelectedStudents.Contains(a)).Select(a => a).ToList();

search arraylist by property using binarysearch

I currently have an arraylist containing classes in C#. The arraylist is filled like this:
foreach (XmlElement Path in locaties)
{
ISoundSource track = engine.AddSoundSourceFromFile(Path.InnerXml);
mixarray.Add(track);
}
then the array has many ISoundSource classes as its items. Now the thing that sets them apart in the array is their 'name' property. Later on I want to get the ISoundSource from the array by doing a search. I looked up on how to search arraylists and it is said to use a binarysearch but I don't see a way to look up an object with a certain property. How can I get the item from the array which has the name I specify?
You should probably use a Dictionary<,> as it will be much easier to maintain. Also, you should use List<> instead of ArrayList. If you must use BinarySearch, you will have to pass it a custom implementation of IComparer in order to have it use the Name property. Here's an example with a dictionary:
var dictionary = new Dictionary<string, ISoundSource>();
foreach (XmlElement Path in locaties)
{
ISoundSource track = engine.AddSoundSourceFromFile(Path.InnerXml);
mixarray.Add(track);
dictionary[track.Name] = track;
}
ISoundSource item = dictionary["MyTrackName"];
Check out the two parameter overload of BinarySearch which takes an IComparer as the second parameter - you then need to create a small class that inherits from IComparer that will compare the names of two of your Track objects, and pass an instance of this comparer into the BinarySearch.
There are many ways to do what you're asking for, and the right way depends on information that you haven't provided:
Does the Name property uniquely identify items?
Does every item have a Name?
Does the match have to be exact?
Is it important to know what order the items were originally added to the list in, i.e. the order that they appear in the source XML?
Are you trying to find items given their Name, or access them in order by their Name?
How important is it that this be efficient?
It may be that the right solution is to simply use LINQ to find an item:
ISoundSource track = mixarray
.Cast<ISoundSource>
.Where(x => x.Name == name)
.FirstOrDefault();
which will set track to the first item in the list whose name matches the value you're looking for, and to null if there's no match found. (If you use a List<ISoundSource> instead of an ArrayList, you can omit the Cast<ISoundSource> - one of many, many reasons to use List<T> over ArrayList in most cases.)
Most of the time I'll use a Dictionary<TKey, TValue> for this kind of thing, but that's because most the time the answers to those questions are yes, yes, yes, no, don't care about the order, pretty important.
For posterity, here is an alternative way to generate a dictionary using a simple Linq expression.
var dictionary = locaties
.Select(p->engine.AddSoundSourceFromFile(Path.InnerXml))
.ToDictionary(t->t.Name);
The .Select() transforms each node into an ISoundSource. When done, a collection (IEnumerable of ISoundSource) is returned. The .ToDictionary() then converts that list of ISoundSource to a Dictionary of string, ISoundSource.
This requires .NET Framework 3.5 or higher.

Categories

Resources