2012-03-08 38 views
5

Tôi có điều khiển ListView hiển thị các mục từ bộ sưu tập có thể quan sát được. Các mục này cần phải được lọc. Tôi có thể làm điều đó với CollectionViewSource, nhưng bộ lọc cần phải được cập nhật mỗi khi một mục thay đổi.Lọc bộ sưu tập có thể quan sát

mục của tôi trông như thế này:

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; } 
    } 
} 

Sau đó, tôi có ListView này có ItemSource là ràng buộc để bộ sưu tập.

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

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

Điều này không lọc bất cứ điều gì. Vì vậy, tôi có các hộp kiểm tra và họ nên chỉ ra các mục (tùy thuộc vào nhà nước) nên được hiển thị. Tôi đã sử dụng sau đó một CollectionViewSource:

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

Phương pháp lọc trông như thế này:

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); 
} 

này có những bất lợi mà nó còn thể hiện giá trị của các hộp kiểm, vì vậy tôi phải gọi phương pháp này (UpdateView) mỗi khi một hộp kiểm được chọn. Nhưng nó đã có tác dụng.

Tuy nhiên, trạng thái mục sẽ thay đổi và nếu "hoàn thành" không được chọn chẳng hạn, khi một mục được "hoàn thành", nó sẽ bị xóa khỏi chế độ xem. Rõ ràng là điều đó không thay đổi trừ khi tôi lại gọi UpdateView. Vì vậy, tôi cần phải gọi phương pháp này mỗi khi có điều gì đó thay đổi. Điều đó có vẻ xấu xí và không hiệu quả với tôi.

Vì vậy, câu hỏi của tôi là, điều này có thể được thực hiện một cách đẹp hơn không?

+0

Tôi đã đăng một cách tiếp cận khác làm câu trả lời nhưng tôi nhớ việc thực hiện các bộ lọc hoạt động mà không cần gọi hàm Update(). Hãy thử triển khai NotifyPropertyChanged trong Project - ràng buộc không nhận thức được sự thay đổi mà không có nó. – Paparazzi

Trả lời

13

Bind ListView bạn trực tiếp đến bộ sưu tập được lọc thay vì ObservableCollection bằng cách tạo ra một tài sản -

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

Vì vậy, bây giờ chỉ đơn giản là bạn cần phải gọi Refresh() trên bộ sưu tập của bạn vào trạng thái hộp kiểm của bạn sự kiện đã thay đổi như thế này -

YourFilteredCollection.Refresh(); 

Để làm mới bộ sưu tập dựa trên bất kỳ thay đổi trạng thái nào trong hạng mục, bạn có thể khái quát hóa nó bằng cách móc sự kiện PropertyChanged của lớp mục của bạn (ví này lớp học của bạn cần phải thực hiện INotifyPropertyChanged) và từ đó bạn có thể gọi làm mới như thế này -

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

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

Vì vậy, bất cứ khi nào bất kỳ thay đổi tài sản trong lớp hàng của bạn, bộ sưu tập của bạn sẽ được lọc .

+1

nhưng tôi vẫn phải gọi Làm mới mỗi lần trạng thái của một mục hoặc các hộp kiểm thay đổi. –

+0

Tôi đã cập nhật câu trả lời của mình về mối lo ngại của bạn về thay đổi trạng thái.Vì vậy, theo cách này, bạn cần phải gọi lại chỉ làm mới từ hai địa điểm ngay bây giờ. Hi vọng điêu nay co ich. Hơn nữa, bạn có thể phân lớp ObservableCollection và di chuyển logic này ở đó và có thể sử dụng các lớp con của bạn thay cho ObservableCollection của bạn. –

+1

Điều này có thể giúp bạn phân lớp ObservableCollection - http://msdn.microsoft.com/en-us/library/ee696421.aspx –

4

Tôi thích sử dụng DataTriggers cho điều đó. Đối với logic của bạn sẽ cần phải sử dụng một công cụ chuyển đổi đa giá trị.

<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

Sử dụng công cụ như ContinuousLinq. Bạn ràng buộc listview của bạn với một truy vấn sẽ đánh giá lại khi một mục trong danh sách (hoặc chính danh sách) thay đổi.

Các vấn đề liên quan