How is ICollectionViewLiveShaping
implemented for the purpose of filtering? Is it something like:
public ICollectionView WorkersEmployed { get; set;
All you need to do is add a property
in LiveFilteringProperties
for which you want the filter to call on property change and set IsLiveFiltering
to true
for your collection to enable live filtering
.
Make sure PropertyChanged
event gets raised whenever EmployerID
property changes i.e. your Worker
class should implement INotifyPropertyChangedEvent
.
This will work then -
public ICollectionViewLiveShaping WorkersEmployed { get; set; }
ICollectionView workersCV = new CollectionViewSource
{ Source = GameContainer.Game.Workers }.View;
ApplyFilter(workersCV);
WorkersEmployed = workersCV as ICollectionViewLiveShaping;
if (WorkersEmployed.CanChangeLiveFiltering)
{
WorkersEmployed.LiveFilteringProperties.Add("EmployerID");
WorkersEmployed.IsLiveFiltering = true;
}
I experimented with this and it looks like it is not designed for what you (and me) want: Automatic filtering when you change filtering conditions. It filters automatically when some properties of DataGrid's item source changes, but not when filter conditions change - you must call ICollectionViewSource.Refresh manually.
We are using WPF + MVVM + Visual Studio 2017.
We want to convert this to add live filtering:
public ObservableCollection<RowViewModel> Rows { get; set; }
The method below has two key advantages:
Please let me know if this worked for you, any issues and I'll update the instructions to make easier.
And the steps:
Create a special ObservableCollection that does not fire update events. This is a one-off. We want to fire the update bulk update event ourselves, which is faster.
public class NonNotifyingObservableCollection<T> : ObservableCollection<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { /* Do nothing */ }
}
Convert to a private variable which uses this new collection.
private NonNotifyingObservableCollection<RowViewModel> rows;
// ... and in constructor
rows = new NonNotifyingObservableCollection<RowViewModel>();
Add these variables:
private ICollectionView rowsView;
public ICollectionViewLiveShaping RowsLiveView { get; set; }
And in the Initialise() call after the ViewModel is constructed (or perhaps in the constructor):
// Call on the dispatcher.
dispatcher.InvokeAsync(() =>
{
this.rowsView = CollectionViewSource.GetDefaultView(this.rows);
this.rowsView.Filter = o =>
{
// This condition must be true for the row to be visible on the grid.
return ((RowViewModel)o).IsVisible == true;
};
this.RowsLiveView = (ICollectionViewLiveShaping)this.rowsView;
this.RowsLiveView.IsLiveFiltering = true;
// For completeness. Changing these properties fires a change notification (although
// we bypass this and manually call a bulk update using Refresh() for speed).
this.RowsLiveView.LiveFilteringProperties.Add("IsVisible");
});
Now we add items to the backing collection, then call .Refresh()
to refresh the view:
this.rowsView.Add(new RowViewModel( /* Set properties here. */ ));
We then bind the grid to RowsLiveView
, (instead of binding to Rows
in the original code).
Now we can update the IsVisible
property, then call .Refresh()
to redraw the grid.
rows[0].IsVisible=false;
this.rowsView.Refresh(); // Hides the first row.
Update: This answer could be simplified. The whole point of ICollectionViewLiveShaping
is to autorefresh without the need to call .Refresh()
. Given that we have a NonNotifyingObservableCollection
and we are manually controlling everything with a .Refresh()
, could remove public ICollectionViewLiveShaping RowsLiveView { get; set; }
and, directly to RowsView
(make it a property with { get; set; }
, and use normal ObservableCollection<>
. In other words - ICollectionViewLiveShaping is great for a small amount of rows (e.g. <100), but for anything more, ICollectionView
in combination with a bulk update and a manual Refresh()
is better from a speed point of view.