过滤一个可观察的集合

问题描述:

我有一个 ListView 控件,显示可观察集合中的项目。这些项目需要过滤。我可以使用 CollectionViewSource 执行此操作,但每次更改项目时,需要更新过滤器。

I have a ListView control that displays items from an observable collection. These items need to be filtered. I am able to do that with a CollectionViewSource, but the filter needs to be updated each time an item changes.

我的项目如下所示:

enum Status {Done, Failed, Skipped, ...}

class Project {
  public string Name {get;set;}
  public Status Status {get;set;}
  // etc. etc.
}

class ProjectViewModel : INotifyPropertyChanged {
  private Project project;

  public ProjectBuildInfoViewModel(ProjectBuildInfo project)
  {
    this.project = project;
  }

  public string Name
  {
     get { return project.Name; }
     set { project.Name = value; OnPropertyChanged("Name"); }
  }

  // etc. etc.
}

class CollectionViewModel {
  private ObservableCollection<ProjectViewModel> projects = 
             new ObservableCollection<ProjectViewModel>();

  public ObservableCollection<ProjectViewModel> Collection
  {
     get { return projects; }
     private set {projects = value; }
  } 
}

然后我有这个 ListView ItemSource 绑定到集合。

Then I have this ListView whose ItemSource is bound to the collection.

// member of the user control class
private CollectionViewModel collection = new CollectionViewModel();

// in the constructor
listView.ItemSource = collection.Collection.

这不过滤任何东西。所以我有这些复选框,应该显示哪些项目(取决于状态)。我已经使用了一个 CollectionViewSource

This doesn't filter anything. So I have these check boxes and they should indicate which items (depending of the state) should be displayed. I have used then a CollectionViewSource:

private void UpdateView()
{
  var source = CollectionViewSource.GetDefaultView(collection.Collection);
  source.Filter = p => Filter((ProjectViewModel)p);
  listStatus.ItemsSource = source;
}

过滤器方法如下所示:

private bool Filter(ProjectViewModel project)
{
     return (ckFilterDone.IsChecked.HasValue && ckFilterDone.IsChecked.Value && project.Status == Status.Done) ||
            (ckFilterFailed.IsChecked.HasValue && ckFilterFailed.IsChecked.Value && project.Status == Status.Failed) ||
            (ckFilterSkipped.IsChecked.HasValue && ckFilterSkipped.IsChecked.Value && project.Status == Status.Skipped);
}

这个缺点是它捕获了复选框的值,所以我有每次选中一个复选框时,调用此方法( UpdateView )。但是它的工作原理。

This has the disadvantage that it captures the values of the checkboxes, so I have to call this method (UpdateView) each time a checkbox is checked. But it works.

但是,项目状态确实会改变,如果没有检查完成,当一个项目进入完成时,应该从风景。显然这不会改变,除非我再次调用 UpdateView 。所以我需要每次改变时都调用这个方法。这对我来说看起来很丑陋,效率不高。

However, the item state does change and if "done" is not checked for instance, when an item goes into "done" it should be removed from the view. Obviously that doesn't change unless I again call UpdateView. So I need to call this method each time something changes. That looks ugly and inefficient to me.

所以我的问题是,可以以更好的方式完成吗?

So my question is, can this be done in a nicer way?

通过创建属性将 ListView 直接绑定到过滤的集合而不是ObservableCollection中。

Bind your ListView directly to the filtered collection instead of the ObservableCollection by creating a property -

public ICollectionView YourFilteredCollection
{
   get
   {      
      var source = CollectionViewSource.GetDefaultView(collection.Collection);
      source.Filter = p => Filter((ProjectViewModel)p);
      return source;
   }
}

所以,现在只需要调用Refresh在您的复选框上的收藏状态改变了这样的事件 -

So, now simply you need to call Refresh() on your collection on your check boxes state changed event like this -

YourFilteredCollection.Refresh();

要根据项目类中的任何状态更改刷新集合,您可以通过挂钩 PropertyChanged 您的项目类的事件(为此您的课程需要实现INotifyPropertyChanged),从那里可以调用这样刷新 -

To refresh the collection based on any state change in the item class, you can generalize it by hooking the PropertyChanged event of your item class (for this your class need to implement INotifyPropertyChanged) and from there you can call refresh like this -

foreach (YourClass item in collection.Collection)
{
  item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}

void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  YourFilteredCollection.Refresh();
}

所以,每当物品类别中的任何属性发生变化时,您的收藏集将被过滤。

So, whenever any property changes in your item class, your collection will be filtered.