2012-03-08 17 views
5

私には、観察可能なコレクションのアイテムを表示するListViewコントロールがあります。これらの項目はフィルタリングする必要があります。 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; } 
    } 
} 

その後、私はそのItemSourceコレクションにバインドされているこのListViewを持っています。

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

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

これは何もフィルタリングしません。だから、私はこれらのチェックボックスを持っており、どの項目(状態に依存する)を表示すべきかを示すはずです。私はその後、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 )チェックボックスがチェックされるたびに。しかし、それは動作します。

しかし、アイテムの状態は変更されます。たとえば、「完了」がチェックされていない場合、アイテムが「完了」になるとビューから削除する必要があります。明らかに、私が再びUpdateViewと呼ぶまでそれは変わらない。だから私は何かが変わるたびにこのメソッドを呼び出す必要があります。それは私には醜く非効率的に見えます。

私の質問は、これはより良い方法で行うことができますか?

+0

私は答えとして別のアプローチを掲載しましたが、Update()を呼び出さずにフィルタを動作させることを思い出しています。 ProjectでNotifyPropertyChangedを実装してみてください。バインディングは、NotifyPropertyChangedなしで変更を認識しません。 – Paparazzi

答えて

13

- だから、

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

は、今、単にあなたのチェックボックスの状態にあなたのコレクションに更新()を呼び出す必要がありますこのような変更されたイベント -

YourFilteredCollection.Refresh(); 

アイテムクラスのいずれかの状態変化に基づいてコレクションをリフレッシュするには、あなたはフックして、それを一般化することができますあなたの項目クラスの任意のプロパティの変更は、あなたのコレクションがフィルタリングされるたび、そう

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

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

- アイテムクラスのPropertyChangedイベントとそこからあなたがこのようなリフレッシュ呼び出すことができます(これはあなたのクラスのためには、INotifyPropertyChangedのを実装する必要があります) 。

+1

しかし、アイテムの状態が変わるたびに、またはチェックボックスが変わるたびにRefreshを呼び出す必要があります。 –

+0

私はあなたの状態の変化に関する懸念に関する私の答えを更新しました。したがって、この方法では、リフレッシュを2つの場所から呼び出す必要があります。お役に立てれば。さらに、ObservableCollectionをサブクラス化してこのロジックをそこに移動し、ObservableCollectionの代わりにサブクラスを使用することができます。 –

+1

これはObservableCollectionをサブクラス化するのに役立つかもしれません - http://msdn.microsoft.com/en-us/library/ee696421.aspx –

4

私はそれにDataTriggersを使いたいと思います。あなたのロジックは多値コンバータを使用する必要があります。プロパティ作成することによって、代わりのObservableCollectionのフィルター処理されたコレクションに直接ごListViewバインド

<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> 
1

ContinuousLinqのようなツールを使用します。リストビュー(またはリスト自体)の項目が変更されたときに再評価されるクエリにリストビューをバインドします。

関連する問題