2009-08-21 25 views
15

tôi có một cái nhìn cây giả có chứa dữ liệu này:Làm cách nào để lọc cấu trúc phân cấp wpf treeview bằng ICollectionView?

RootNode 
    Leaf 
    vein 
SecondRoot 
    seeds 
    flowers 

Tôi cố gắng để lọc các nút để chỉ hiển thị các nút có chứa một văn bản nào đó. Nói nếu tôi chỉ định "L", cây sẽ được lọc và chỉ hiển thị RootNode-> Leaf và SecondRoot-> flowers (vì cả hai đều chứa chữ cái L).

Tiếp theo mô hình mv-vm, tôi có một lớp cơ bản TreeViewViewModel như thế này:

public class ToolboxViewModel 
{ 
    ... 
    readonly ObservableCollection<TreeViewItemViewModel> _treeViewItems = new ObservableCollection<TreeViewItemViewModel>(); 
    public ObservableCollection<TreeViewItemViewModel> Headers 
    { 
     get { return _treeViewItems; } 
    } 

    private string _filterText; 
    public string FilterText 
    { 
     get { return _filterText; } 
     set 
     { 
      if (value == _filterText) 
       return; 

      _filterText = value; 

      ICollectionView view = CollectionViewSource.GetDefaultView(Headers); 
      view.Filter = obj => ((TreeViewItemViewModel)obj).ShowNode(_filterText); 
     } 
    } 
    ... 
} 

Và một TreeViewItemViewModel cơ bản:

public class ToolboxItemViewModel 
{ 
    ... 
    public string Name { get; private set; } 
    public ObservableCollection<TreeViewItemViewModel> Children { get; private set; } 
    public bool ShowNode(string filterText) 
    { 
     ... return true if filterText is contained in Name or has children that contain filterText ... 
    } 
    ... 
} 

Tất cả mọi thứ được thiết lập trong XAML vì vậy tôi thấy treeview và hộp tìm kiếm.

Khi mã này được thực hiện, bộ lọc chỉ áp dụng cho các nút Gốc không đủ. Có cách nào để làm cho bộ lọc chảy xuống trong hệ thống phân cấp của các nút sao cho vị từ của tôi được gọi cho mỗi nút? Nói cách khác, bộ lọc có thể được áp dụng cho toàn bộ TreeView không?

+2

cậu kết thúc gì lên làm ? Bất kỳ thông tin hiệu suất nào bạn có thể chuyển tiếp hoặc giải pháp khác? –

Trả lời

3

Thật không may là không có cách nào để làm cho cùng một bộ lọc áp dụng cho tất cả các nút một cách tự động. Bộ lọc là thuộc tính (không phải là DP) của ItemsCollection không phải là DependencyObject và do đó thừa kế giá trị DP không có ở đó.

Mỗi nút trong cây có ItemsCollection riêng có Bộ lọc riêng. Cách duy nhất để làm cho nó hoạt động là tự thiết lập tất cả để gọi cùng một đại biểu.

Cách đơn giản nhất là để hiển thị thuộc tính bộ lọc của loại Predicate < đối tượng > tại ToolBoxViewModel của bạn và trong setter của nó kích hoạt sự kiện. Sau đó ToolboxItemViewModel sẽ chịu trách nhiệm về việc tiêu thụ sự kiện này và cập nhật Filter của nó.

Aint đẹp và tôi không chắc chắn về hiệu suất sẽ như thế nào đối với số lượng lớn các mục trong cây.

2

Cách duy nhất tôi đã tìm thấy để làm điều này (mà là một chút của một hack), là tạo ra một ValueConverter chuyển đổi từ IList để IEnumerable. trong ConvertTo(), trả về một CollectionViewSource mới từ được truyền vào trong IList.

Nếu có cách nào tốt hơn để làm điều đó, tôi rất muốn nghe nó. Điều này dường như làm việc, mặc dù.

0

Bạn có thể lấy TreeViewItem cho một phần tử đã cho trong một cây bằng cách sử dụng ItemContainerGenerator và một khi bạn có rằng bạn đang ở một vị trí để đặt Bộ lọc.

6

Đây là cách tôi lọc các mục trên TreeView tôi:

Tôi có lớp:

class Node 
{ 
    public string Name { get; set; } 
    public List<Node> Children { get; set; } 

    // this is the magic method! 
    public Node Search(Func<Node, bool> predicate) 
    { 
     // if node is a leaf 
     if(this.Children == null || this.Children.Count == 0) 
     { 
      if (predicate(this)) 
       return this; 
      else 
       return null; 
     } 
     else // Otherwise if node is not a leaf 
     { 
      var results = Children 
           .Select(i => i.Search(predicate)) 
           .Where(i => i != null).ToList(); 

      if (results.Any()){ 
       var result = (Node)MemberwiseClone(); 
       result.Items = results; 
       return result; 
      } 
      return null; 
     }    
    } 
} 

Sau đó, tôi có thể lọc kết quả như sau:

// initialize Node root 
// pretend root has some children and those children have more children 
// then filter the results as: 
var newRootNode = root.Search(x=>x.Name == "Foo"); 
Các vấn đề liên quan