过滤可观察的集合

Mar*_*ila 7 data-binding wpf listview filtering observablecollection

我有一个ListView控件,显示可观察集合中的项目.这些项目需要过滤.我可以用a做CollectionViewSource,但每次项目更改时都需要更新过滤器.

我的项目看起来像这样:

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; }
  } 
}
Run Code Online (Sandbox Code Playgroud)

然后,我有这个ListView,其ItemSource是绑定到集合.

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

// in the constructor
listView.ItemSource = collection.Collection.
Run Code Online (Sandbox Code Playgroud)

这不会过滤任何东西.所以我有这些复选框,他们应该指出应该显示哪些项目(取决于州).我用过了一个CollectionViewSource:

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

过滤方法如下所示:

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);
}
Run Code Online (Sandbox Code Playgroud)

这样做的缺点是它会捕获复选框的值,因此UpdateView每次选中复选框时都必须调用此方法().但它的确有效.

但是,项目状态确实发生了变化,例如,如果没有检查"完成",当项目进入"完成"时,它应该从视图中删除.显然,除非我再次打电话,否则不会改变UpdateView.所以我需要在每次更改时调用此方法.这对我来说看起来很丑陋而且效率低下.

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

Roh*_*ats 19

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

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

所以,现在只需要在复选框上调用您的集合上的Refresh()状态更改事件,如下所示 -

YourFilteredCollection.Refresh();
Run Code Online (Sandbox Code Playgroud)

要根据项类中的任何状态更改来刷新集合,可以通过挂钩PropertyChanged项类的事件来对其进行概括(为此,您的类需要实现INotifyPropertyChanged),然后您可以在此处调用refresh -

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

void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
  YourFilteredCollection.Refresh();
}
Run Code Online (Sandbox Code Playgroud)

因此,只要您的商品类中的任何属性发生更改,您的收藏就会被过滤.


pap*_*zzo 5

我喜欢使用DataTriggers.因为你的逻辑需要使用多值转换器.

 <ListView Grid.Row="3" Grid.Column="2" ItemsSource="{Binding Path=GabeLib.DocFieldsAll}">
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}"  >
                <Style.Triggers>
                    <DataTrigger Binding="{Binding Path=Active}" Value="False">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding Path=FieldDef.ID}" Value="0">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListView.ItemContainerStyle>
Run Code Online (Sandbox Code Playgroud)