How to cope with an Observable collection of classes - c#

I have following TracefieldPartProgramClass
public class TracefieldPartProgramClass
{
public TracefieldPartProgramClass() { }
public ObservableCollection<Tuple<string, object, object>> obcTraceFieldPartProgram = new ObservableCollection<Tuple<string, object, object>>();
}
I use it to make the following collection:
ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>();
now after having filled it I want to be able to sort as I want (say on the Tracefield[0]).
So I implemented this:
private ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> SortOnTracefield(ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> obcToSort)
{
var obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>();
obcSorted = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString());<--- this is where I get the error
return obcSorted;
}
but when I do it, I get this error:
Error CS0266 Cannot implicitly convert type 'System.Linq.IOrderedEnumerable' to 'System.Collections.ObjectModel.ObservableCollection'. An explicit conversion exists (are you missing a cast?)

Try this one:
private ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> SortOnTracefield(ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass> obcToSort)
{
var sorted = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString();
return new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(sorted);
}
You can pass an IEnumerable to the observable collection's constructor.
However, that returns a new instance of the collection. So, if the unsorted instance was bound to the GUI, that may not update your GUI. Another approch to support sorting is to use the ICollectionView.
The SO question how-do-i-sort-an-observable-collection may be intersting for you too. The following extension method can be used for sorting ObservableCollections without recreation:
static class Extensions
{
public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable
{
List<T> sorted = collection.OrderBy(x => x).ToList();
for (int i = 0; i < sorted.Count(); i++)
collection.Move(collection.IndexOf(sorted[i]), i);
}
}
However, your TracefieldPartProgramClass class needs to implement IComparable or you'll need to implement a custom IComparer<TracefieldPartProgramClass> and pass it the Sort method.

As the error says, you are trying to assign an IOrderedEnumerable to a variable of type ObservableCollection, which is not possible.
But luckily, ObservableCollection has a constructor that takes an IEnumerable and prefills it with the items of that IEnumerable.
So, in order to make that error disappear, use that:
var sortedEnumerable = obcToSort.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString());
obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(sortedEnumerable );

Enumerable.OrderBy doesn't return an ObservableCollection but an IEnumerable<T>, you can create a new with the List<T> constructor:
var obcSortedList = obcToSort
.OrderBy(w => w.obcTraceFieldPartProgram[0].Item3.ToString())
.ToList();
var obcSorted = new ObservableCollection<EasyRunBinSerializableData.TracefieldPartProgramClass>(obcSortedList);

Related

IEnumerable<IEnumerable>> and Extension Method

I was wondering if someone could help me understand the following behavior. In the following code, I am creating a CustomObject instance, which contains a single Property of type IEnumerable<IEnunumerable>>.
I also have an extension Method on IEnumerable<T> called AsDataTable.
public class CustomObject
{
public IEnumerable<IEnumerable> Collection {get;set;}
}
public static class ExtensionMethods
{
public static bool AsDataTable<T>(this IEnumerable<T> list)
{
Console.Write("In extension method");
return default(bool);
}
}
void Main()
{
var ttObject = new CustomObject()
{
Collection = new List<IEnumerable>
{
new List<int>{1,2,3},
new [] {new{A="abc",B="def"}}
}
};
var dummy = new []{new {a='r'}}.AsDataTable();
foreach(var item in ttObject.Collection)
{
var temp = item.AsDataTable();
Console.WriteLine($"Item is IEnumerable : {item is IEnumerable}");
}
}
What makes me wonder if the following line of code works (or rather compiles)
var dummy = new []{new {a='r',b='3'}}.AsDataTable();
while when I loop over the Collection Property of CustomObject and then do the same it doesn't allow me to compile.
var temp = item.AsDataTable(); // this doesn't work
Curiously the following line returns true reconfirming 'item' is indeed IEnumerable.
Console.WriteLine($"Item is IEnumerable : {item is IEnumerable}");
I guess it is because the extension method is written over Generic version IEnumerable<T>, but then how is it that it works over the anonymous type array (outside the CustomObject).
IEnumerable<T> implements IEnumerable, not vice versa.
Through a bit of runtime hacking, SomeType[] actually does implement IEnumerable<SomeType>. On the other hand, IEnumerable doesn't - and overload resolution is done at compile time, so the compiler has no idea that your IEnumerable items in the collection actually also implement IEnumerable<int>.
If you need to work with IEnumerable, you need to use that in your extension method.

optimise code into linq expression

I have an observable collection I want to remove an element from. I have tried using the linq .Where syntax but i am getting stuck with the casting.
public class Atc
{
public string Code { get; set; }
public string Description { get; set; }
public string ParentDescriptions { get; set; }
}
i have defined the observable collection of medicines,
public ObservableCollection<Atc> MedicinesObservableCollection
and i have a method to filter out the item from the collection that works:
private void RemoveMedicineListItem(string code)
{
var filteredCollection = new ObservableCollection<Atc>();
foreach (var item in MedicinesObservableCollection)
{
if (item.Code != code)
{
filteredCollection.Add(item);
}
}
MedicinesObservableCollection = filteredCollection;
}
I have tried:
(ObservableCollection<Atc>)MedicinesObservableCollection.Where(x => x.Code != code);
but this gets a runtime exception
System.InvalidCastException: 'Unable to cast object of type 'WhereEnumerableIterator1[Atc]' to type 'System.Collections.ObjectModel.ObservableCollection1[Atc]'.'
i get this is something to do with the linq enumerables but i am out of my depth.
You can´t cast an instance of WhereIterator<Atc> (which your Where-clause actually returns) to ObservableCollection<Atc>). The returned instance only implements IEnumerable<Atc>, this is you can iterate it. However it doesn´t havy anything in common with any list, or in particular with an observable one.
However you can create a new one based on the returned value:
var result = new ObservableCollection(MedicinesObservableCollection.Where(x => x.Code != code));
All the answers are correct but when using ObservableCollection it would be better to do a remove on the same object rather then creating a new list.
MedicinesObservableCollection.Remove(MedicinesObservableCollection.FirstOrDefault(x => x.Code == code));
This is because recreating the collection might force all the items in the list to be re-rendered and this way only one item will be removed from the same collection.
Linq doesn't return a collection of the same type as the input. See this. So your cast to ObservableCollection<Atc> fails as it's some internal implementation of IEnumerable, not an ObservableCollection.
The solution is just to create a new ObservableCollection:
var items = new ObservableCollection<Atc>(MedicinesObservableCollection.Where(x => x.Code != code));

Convert output of LINQ ToList() to List<T> subclass

I have a class that extends List:
public class MyObjectList : List<MyObject>
{
...
}
Currently, there is a LINQ statement that groups by a key value:
MyObjectList objects = new MyObjectList(); //initializes and loads list
var objectsByKey = objects.GroupBy(obj => obj.MyKey)
.Select(objs => new {MyKey = objs.Key, MyObjs = objs.ToList()})
.ToList();
In the output, MyObjs is of type List< MyObject>, and it lists the correctly grouped objects.
When I try to cast it as MyObjectList, MyObjs ends up being null.
var objectsByKey = objects.GroupBy(obj => obj.MyKey)
.Select(objs => new {MyKey = objs.Key, MyObjs = objs.ToList() as MyObjectList})
.ToList();
How can I get MyObjs to be of type MyObjectList, with the correclty grouped objects?
In your MyObjectList class, provide a constructor like so:
public class MyObjectList : List<MyObject>
{
public MyObjectList(IEnumerable<MyObject> list)
:base(list)
{
}
}
Then, when selecting out your objects, do it like this:
var objectsByKey = objects.GroupBy(obj => obj.MyKey)
.Select(objs => new {MyKey = objs.Key, MyObjs = new MyObjectList(objs)})
.ToList();
Enumerable.ToList (the extension method that you are using) does not know about your custom list type. Casting will not convert it.
You need to instantiate and fill the list yourself. There are at least two ways of doing it:
Constructor
public MyObjectList(IEnumerable<MyObject> collection) : base(collection) {}
You'd have to replace the objs.ToList() call with new MyObjectList(objs)
Extension method
public static class MyObjectListExtensions
{
public static MyObjectList ToList(this IEnumerable<MyObject> collection)
{
//You could also call the constructor described above
var list = new MyObjectList();
list.AddRange(collection);
return list;
}
}
With this, you don't have to change your code, as long as you include the namespace of MyObjectListExtensions in your file. Why? Because being non-generic, MyObjectListExtensions.ToList takes precedence over Enumerable.ToList.
If you don't want MyObjectList to be instantiated always, you could use a different name.

Removing elements from binding list

In one of my projects I'm trying to remove an item from a list where the id is equal to the given id.
I have a BindingList<T> called UserList.
Lists have all the method RemoveAll().
Since I have a BindingList<T>, I use it like that:
UserList.ToList().RemoveAll(x => x.id == ID )
However, my list contains the same number of items as before.
Why it's not working?
It's not working because you are working on a copy of the list which you created by calling ToList().
BindingList<T> doesn't support RemoveAll(): it's a List<T> feature only, so:
IReadOnlyList<User> usersToRemove = UserList.Where(x => (x.id == ID)).
ToList();
foreach (User user in usersToRemove)
{
UserList.Remove(user);
}
We're calling ToList() here because otherwise we'll enumerate a collection while modifying it.
You could try:
UserList = UserList.Where(x => x.id == ID).ToList();
If you use RemoveAll() inside a generic class that you intend to be used to hold a collection of any type object, like this:
public class SomeClass<T>
{
internal List<T> InternalList;
public SomeClass() { InternalList = new List<T>(); }
public void RemoveAll(T theValue)
{
// this will work
InternalList.RemoveAll(x =< x.Equals(theValue));
// the usual form of Lambda Predicate
//for RemoveAll will not compile
// error: Cannot apply operator '==' to operands of Type 'T' and 'T'
// InternalList.RemoveAll(x =&gt; x == theValue);
}
}
This content is taken from here.
If there is just one item as unique ID inside bindinglist, the below simple code can work.
UserList.Remove(UserList.First(x=>x.id==ID));

Using a class derived from generic list with LINQ

I have two classes, CheckboxItemsList which extends a generic list, and CheckboxItems, which contains a list of objects of type CheckboxItem.
I want to use LINQ to be able to filter CheckboxItemsList based on properties of its CheckboxItems objects. The return type is always a generic list, though, but I want it to be a CheckboxItemsList.
So I guess the basic question is, can linq be made to return a list of the same type that it starts with? Since I can't cast a base class to a derived class, do I have any option other than iterating through the results of the linq query and rebuilding the derived list object row by row? Not that this is the end of the world, but I'm relatively new to linq and was wondering it there is a better way to do it.
What I want:
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed);
(obviously doesn't work since the query will return List<CheckboxItems>, not CheckboxItemsList)
The objects, generally:
public class CheckboxItemsList: List<CheckboxItems>
{
// does not add any fields, just access methods
}
public class CheckboxItems : IEnumerable<CheckboxItem>
{
public long PrimaryKey=0;
protected CheckboxItem[] InnerList;
public bool Changed
{
get {
return (InnerList.Any(item => item.Changed));
}
}
....
}
No, this is not possible out of the box. You'll need to add code to do this.
For example, you can add a constructor like so:
public CheckboxItemsList(IEnumerable<CheckboxItems> checkboxItems) {
// something happens
}
Then you can say
CheckboxItemsList newList = new CheckboxItemsList(
MyCheckboxItemsList.Where(item => item.Changed)
);
Additionally, you could add an extension method like so
static class IEnumerableCheckboxItemsExtensions {
public static ToCheckboxItemsList(
this IEnumerable<CheckboxItems> checkboxItems
) {
return new CheckboxItemsList(checkboxItems);
}
}
and then
CheckboxItemsList newList =
MyCheckboxItemsList.Where(item => item.Changed)
.ToCheckboxItemsList();
LINQ works on IEnumerable<T> and IQueryable<T> and the result types of all LINQ operations (Where, Select) etc, will return one of those. The standard ToList function returns a concrete list of type List<T>, you may need to come up with an extension method, e.g.:
public static CheckboxItemsList ToItemList(this IEnumerable<CheckboxItem> enumerable)
{
return new CheckboxItemsList(enumerable);
}
No, there's no built-in way to do this. You have two main options:
Add a constructor to your CheckboxItemsList class that takes an IEnumerable<CheckboxItems> or similar. Pass that collection on to the base List<T> constructor that takes an IEnumerable<T>. That base constructor should then populate the list for you:
var newList =
new CheckboxItemsList(MyCheckboxItemsList.Where(item=>item.Changed));
// ...
public class CheckboxItemsList : List<CheckboxItems>
{
public CheckboxItemsList(IEnumerable<CheckboxItems> collection)
: base(collection)
{
}
}
Create an extension method that takes an IEnumerable<CheckboxItems> or similar and returns a populated CheckboxItemsList:
var newList = MyCheckboxItemsList.Where(item=>item.Changed)
.ToCheckboxItemsList();
// ...
public static class EnumerableExtensions
{
public static CheckboxItemsList ToCheckboxItemsList(
this IEnumerable<CheckboxItems> source)
{
var list = new CheckboxItemsList();
foreach (T item in source)
{
list.Add(item);
}
return list;
}
}
(Of course, for completeness you could implement both of these options. The extension method would then just pass its IEnumerable<CheckboxItems> argument on to the constructor rather than manually looping and adding each item.)
You can also use "Conversion Operator", as below:
public class CheckboxItemsList: List<CheckboxItems>
{
public static implicit operator CheckboxItems(IEnumerable<CheckboxItems> items)
{
var list = new CheckboxItemsList();
foreach (var item in items)
{
list.Add(item);
}
return list;
}
}
Now, the below code would work.
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed);
From MSDN:
A conversion operator declaration that includes the implicit keyword introduces a user-defined implicit conversion. Implicit conversions can occur in a variety of situations, including function member invocations, cast expressions, and assignments. This is described further in Section 6.1.
A conversion operator declaration that includes the explicit keyword introduces a user-defined explicit conversion. Explicit conversions can occur in cast expressions, and are described further in Section 6.2.
Here is what I came up with, building on the various suggestions of others. A generic extension method:
public static T ToList<T>(this IEnumerable baseList) where T : IList,new()
{
T newList = new T();
foreach (object obj in baseList)
{
newList.Add(obj);
}
return (newList);
}
So now I can do what I want:
CheckboxItemsList newList = MyCheckboxItemsList.Where(item=>item.Changed)
.ToList<CheckboxItemsList>();
Another pretty obvious solution occurred to me, which is also useful for situations where the derived list class has field properties that I need to maintain in the new list.
Just create a new instance of my derived list class, and use AddRange to populate it.
// When created with a CheckboxItemsList parameter, it creates a new empty
// list but copies fields
CheckboxItemsList newList = new CheckboxItemsList(OriginalList);
newList.AddRange(OriginalList.Where(item => item.Changed));

Categories

Resources