Reputation: 2874
I have the following simplified classes:
public class BaseContainer
{
public BaseContainer()
{
Children = new ObservableCollection<BaseContainer>();
}
public ObservableCollection<BaseContainer> Children { get; set; }
}
public class ItemA : BaseContainer
{
public ItemA()
{
base.Children.Add(new ItemB() { ItemBName = "bb" });
base.Children.Add(new ItemA() { ItemAName = "ab" });
base.Children.Add(new ItemB() { ItemBName = "ba" });
base.Children.Add(new ItemA() { ItemAName = "aa" });
}
public string ItemAName { get; set; }
}
public class ItemB : BaseContainer
{
public string ItemBName { get; set; }
}
I'm trying to sort my ItemA.Children collection based on two conditions:
So after sorting I'd expect something like this:
Any ideas on how to accomplish this?
I was able to sort by class type name:
List<BaseContainer> temp = base.Children.ToList();
temp.Sort((x, y) => string.Compare(x.GetType().Name, y.GetType().Name));
base.Children.Clear();
base.Children.AddRange(temp);
But Names are not sorted...
Thanks!
Upvotes: 1
Views: 900
Reputation: 112612
You initialize the items by specifying ItemName
, but neither BaseContainer
nor ItemA
nor ItemB
define such a property.
Declare ItemName in the base class
BaseContainer`. The item classes automatically inherit this property and there is no need to redefine such a field there.
public class BaseContainer
{
public string ItemName { get; set; }
...
}
public class ItemA : BaseContainer
{
// Inherits ItemName
}
public class ItemB : BaseContainer
{
// Inherits ItemName
}
Now sort the list like this:
var result = base.Children
.OrderBy(b => b.GetType().Name)
.ThenBy(b => b.ItemName);
And there is no need to store it to a temporary list before sorting. The Result is an IEnumerable<BaseContainer>
. You can append a .ToList()
if you want to store the result as list.
var result = base.Children
.OrderBy(b => b.GetType().Name)
.ThenBy(b => b.ItemName)
.ToList();
UPDATE
According to your comment you don't have a common ItemName
property. Why? You really should have one. Refactor you classes. If for some reason you don't want an ItemName
in BaseContainer
, either create an abstract base class Item
or let the items implement a common interface.
public abstract class Item : BaseContainer
{
public string ItemName { get; set; }
}
public class ItemA : Item
{
}
public class ItemB : Item
{
}
Or
public interface IItem
{
string ItemName { get; set; }
}
public class ItemA : BaseContainer, IItem
{
public string ItemName { get; set; }
}
public class ItemB : BaseContainer, IItem
{
public string ItemName { get; set; }
}
Now you can sort like this
var result = base.Children
.OfType<Item> // or .OfType<IItem>
.OrderBy(i => i.GetType().Name)
.ThenBy(i => i.ItemName);
and the result will be an IEnumerable<Item>
or IEnumerable<IItem>
.
Yeat another option is to override the ToString
method that every object inherits from Syste.Object
.
public class ItemA : BaseContainer
{
public string ItemAName { get; set; }
public override string ToString()
{
return "ItemA: " + ItemAName;
}
}
This simplifies sorting as both conditions (sorting by type and sorting by name) can be done in one step. This string will also be displayed in listboxes, comboboxes etc.
var result = base.Children
.OrderBy(b => b.ToString());
If this is not possible, create and implement an interface for this purpose:
public interface ISortable
{
string SortString { get; }
}
public class ItemA : BaseContainer, ISortable
{
public string ItemAName { get; set; }
public string SortString { get { return "ItemA: " + ItemAName; } }
}
And sort with
var result = base.Children
.OfType<ISortable>
.OrderBy(b => b.SortString);
Upvotes: 1
Reputation: 116168
var result = temp.OrderBy(x => x.GetType().Name).ThenBy(x => GetPropValue(x));
string GetPropValue(object o)
{
Type t = o.GetType();
var p = t.GetProperty("ItemAName");
if (p != null)
{
return (string)p.GetValue(o, null);
}
p = t.GetProperty("ItemBName");
return (string)p.GetValue(o, null);
}
Upvotes: 2
Reputation:
This will get all the A's, order them by AName, then concat with all the B's ordered by B name.
var result = Children.OfType<ItemA>().OrderBy(a => a.ItemAName).Cast<Base>()
.Concat(Children.OfType<ItemB>().OrderBy(b => b.ItemBName));
I'm not sure this is the design I would go with... you might consider adding something to the base class to facilitate this kind of sorting.
Upvotes: 1