Search code examples
wpfcollectionviewsourceicollectionview

Filtering CollectionViewSource with multiple filters


I was reading this: https://social.technet.microsoft.com/wiki/contents/articles/26673.wpf-collectionview-tips.aspx and have tried to apply it to my code without success.

PendingTrucks = (CollectionView)new CollectionViewSource { Source = TruckLog }.View;
ParkedTrucks = (CollectionView)new CollectionViewSource { Source = TruckLog }.View;


PendingTrucks.Filter += PendingTrucks_Filter;
ParkedTrucks.Filter += PendingTrucks_Filter;

public static ICollectionView PendingTrucks
        {
            get; set;
        }

        public static ICollectionView ParkedTrucks
        {
            get; set;
        }



static bool PendingTrucks_Filter (object value)
{
            if (value is Truck truck)
            {
                return truck.ParkItem is null;
            }

            // Fallbackvalue 
            return true;
        }

        static bool ParkedTrucks_Filter (object value)
        {
            if (value is Truck truck)
            {
                return truck.status == 2;
            }

            // Fallbackvalue 
            return true;
        }


I am getting the following error: NullReferenceException: Object reference not set to an instance of an object..

The error shows when it tried to load the form with the Datagrid with the itemsource bound to the view.

This is the call stack I am getting:

This exception was originally thrown at this call stack:
    System.Windows.Data.ListCollectionView.CanAddNew.get()
    System.Windows.Controls.ItemCollection.System.ComponentModel.IEditableCollectionView.CanAddNew.get()
    System.Windows.Controls.DataGrid.OnCoerceCanUserAddOrDeleteRows(System.Windows.Controls.DataGrid, bool, bool)
    System.Windows.Controls.DataGrid.OnCoerceCanUserAddRows(System.Windows.DependencyObject, object)
    System.Windows.DependencyObject.ProcessCoerceValue(System.Windows.DependencyProperty, System.Windows.PropertyMetadata, ref System.Windows.EntryIndex, ref int, ref System.Windows.EffectiveValueEntry, ref System.Windows.EffectiveValueEntry, ref object, object, object, System.Windows.CoerceValueCallback, bool, bool, bool)
    System.Windows.DependencyObject.UpdateEffectiveValue(System.Windows.EntryIndex, System.Windows.DependencyProperty, System.Windows.PropertyMetadata, System.Windows.EffectiveValueEntry, ref System.Windows.EffectiveValueEntry, bool, bool, System.Windows.OperationType)
    System.Windows.DependencyObject.CoerceValue(System.Windows.DependencyProperty)
    System.Windows.Controls.DataGrid.OnItemsSourceChanged(System.Collections.IEnumerable, System.Collections.IEnumerable)
    System.Windows.Controls.ItemsControl.OnItemsSourceChanged(System.Windows.DependencyObject, System.Windows.DependencyPropertyChangedEventArgs)
    System.Windows.DependencyObject.OnPropertyChanged(System.Windows.DependencyPropertyChangedEventArgs)
    ...
    [Call Stack Truncated]

Solution

  • Try this:

    This is the source collection of the ICollectionView. You add/remove items from the collection, and the ICollectionView is updated automatically.

    private ObservableCollection<Truck> Trucks { get; }
    

    Your collection views which bind to the DataGrid:

    public ICollectionView PendingTrucksCollectionView { get; }
    public ICollectionView ParkedTrucksCollectionView { get; }
    

    Initialize the source collection and views on the ViewModel constructor. Here you can pass an object that will populate the observable collection, or an IEnumerable to fill the truck collection.

    public ViewModel() : this(Enumerable.Empty<Truck>())
    { }
    
    
    public ViewModel(IEnumerable<Truck> trucks)
    {
        // Collection Source
        Trucks = new ObservableCollection<Truck>(trucks);
    
        // Collection Views
        PendingTrucksCollectionView = CollectionViewSource.GetDefaultView(Trucks);
        ParkedTrucksCollectionView = CollectionViewSource.GetDefaultView(Trucks);
    
        // Collection View Filters
        PendingTrucksCollectionView.Filter = PendingTrucksFilter;
        ParkedTrucksCollectionView.Filter = ParkedTrucksFilter;
    }
    
    private bool PendingTrucksFilter(object value)
    {
        return value is Truck truck && truck.ParkItem is null;
    }
    
    private bool ParkedTrucksFilter(object value)
    {
        return value is Truck truck && truck.status == 2;
    }
    


    Hope this helps.