2

I have a a collectoin of an object called ItemType, each of which has a child collection of Item. The top level collection is wrapped into an ObservableCollection so it responds when users add or remove things from the collection. This is bound to a TreeView so that each ItemType displays its child Items underneath.

What I'd like to be able to do is use a Filter to get rid of child Item objects that are set to deleted. I'm struggling, because Filter needs a boolean predicate and, of course, only the top-level ItemType gets passed in. E.g:

public void UpdateObservableCollection()
{
    QuoteItemTypesView = CollectionViewSource.GetDefaultView(QuoteItemTypes);
    QuoteItemTypesView.Filter = FilterDeleted;
}

public bool FilterDeleted(object item)
{
    ItemType it = item as ItemType; // only ItemType is ever passed in
    if(it.IsDeleted)
    {
        return false;
    }
    return true;
}   

Is no good because it's removing the ItemType, rather than the any of the Items underneath.

I've tried doing this:

public bool FilterDeleted(object item)
{
    ItemType it = item as ItemType;
    var itemsToRemove = new List<Item>();
    foreach (Item i in it.Items)
    {
        if (i.IsDeleted)
        {
            itemsToRemove.Add(i);
        }
    }

    foreach (var foo in meh)
    {
        it.Items.Remove(foo);
    }

    return true;
}

But this ends up actually removing the items from the underlying collection rather than performing an actual filter.

Is there any way I can filter the child collection?

Bob Tway
  • 9,301
  • 17
  • 80
  • 162

1 Answers1

1

Assume that your ItemType is declared as

public class ItemType : INotifyPropertyChanged
{
    public string Name { get; set; // Raise property changed event }
    public string IsDeleted { get; set; // Raise property changed event }

    //// Other properties

    public List<ItemType> Children { get; set; }

    //// Filter based on provided perdicate
    public Node Search(Func<Node, bool> predicate)
    {
         if(this.Children == null || this.Children.Count == 0)
         {
             if (predicate(this))
                return this;
             else
                return null;
         }
         else 
         {
             var results = Children
                               .Select(i => i.Search(predicate))
                               .Where(i => i != null).ToList();

             if (results.Any()){
                var result = (Node)MemberwiseClone();
                result.Items = results;
                return result;
             }
             return null;
         }             
    }
}

Then you could filter results as:

public bool FilterDeleted(object item)
{
    ItemType it = item as ItemType; // only ItemType is ever passed in
    it = it.Search(x=> x.IsDeleted);
    return true;
} 
user1672994
  • 10,509
  • 1
  • 19
  • 32
  • +1 - this is brilliant. Unfortunately I can't use it because ItemType is an EF object - I can't access its child items until it's created :( – Bob Tway Nov 24 '15 at 10:03
  • 1
    As per best practices, you should map the EF model to ViewModel so that presentation layer only bound to ViewModel and has no direct usage of EF/DB model. – user1672994 Nov 24 '15 at 10:18
  • Yes. Learning why it's best practice a little late in the day. – Bob Tway Nov 24 '15 at 10:23