原文:
Seaching TreeVIew WPF
项目中有一个树形结构的资源,需要支持搜索功能,搜索出来的结果还是需要按照树形结构展示,下面是简单实现的demo。
1.首先创建TreeViewItem的ViewModel,一般情况下,树形结构都包含DisplayName,Deepth,Parent,Children,Id, IndexCode,Visibility等属性,具体代码如下所示:
1 using System; 2 using System.Collections.Generic; 3 using System.Collections.ObjectModel; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using System.Windows; 8 9 namespace TreeViewDemo 10 { 11 public class TreeViewItemVM : NotifyPropertyChangedBase 12 { 13 public TreeViewItemVM () 14 { 15 Visible = Visibility.Visible; 16 } 17 18 private TreeViewItemVM parent; 19 public TreeViewItemVM Parent 20 { 21 get 22 { 23 return this.parent; 24 } 25 set 26 { 27 if (this.parent != value) 28 { 29 this.parent = value; 30 this.OnPropertyChanged(() => this.Parent); 31 } 32 } 33 } 34 35 private ObservableCollection<TreeViewItemVM> children; 36 public ObservableCollection<TreeViewItemVM> Children 37 { 38 get 39 { 40 return this.children; 41 } 42 set 43 { 44 if (this.children != value) 45 { 46 this.children = value; 47 this.OnPropertyChanged(() => this.Children); 48 } 49 } 50 } 51 52 private string id; 53 public string ID 54 { 55 get 56 { 57 return this.id; 58 } 59 set 60 { 61 if (this.id != value) 62 { 63 this.id = value; 64 this.OnPropertyChanged(() => this.ID); 65 } 66 } 67 } 68 69 private string indexCode; 70 public string IndexCode 71 { 72 get { return indexCode; } 73 set 74 { 75 if (indexCode != value) 76 { 77 indexCode = value; 78 this.OnPropertyChanged(() => IndexCode); 79 } 80 } 81 } 82 83 private string displayName; 84 public string DisplayName 85 { 86 get 87 { 88 return this.displayName; 89 } 90 set 91 { 92 if (this.displayName != value) 93 { 94 this.displayName = value; 95 this.OnPropertyChanged(() => this.DisplayName); 96 } 97 } 98 } 99 100 private int deepth; 101 public int Deepth 102 { 103 get 104 { 105 return this.deepth; 106 } 107 set 108 { 109 if (this.deepth != value) 110 { 111 this.deepth = value; 112 this.OnPropertyChanged(() => this.Deepth); 113 } 114 } 115 } 116 117 private bool hasChildren; 118 public bool HasChildren 119 { 120 get 121 { 122 return this.hasChildren; 123 } 124 set 125 { 126 if (this.hasChildren != value) 127 { 128 this.hasChildren = value; 129 this.OnPropertyChanged(() => this.HasChildren); 130 } 131 } 132 } 133 134 private NodeType type; 135 public NodeType Type 136 { 137 get { return type; } 138 set 139 { 140 if (type != value) 141 { 142 type = value; 143 OnPropertyChanged(() => this.Type); 144 } 145 } 146 } 147 148 private Visibility visible; 149 public Visibility Visible 150 { 151 get { return visible; } 152 set 153 { 154 if (visible != value) 155 { 156 visible = value; 157 OnPropertyChanged(() => this.Visible); 158 } 159 } 160 } 161 162 public bool NameContains(string filter) 163 { 164 if (string.IsNullOrWhiteSpace(filter)) 165 { 166 return true; 167 } 168 169 return DisplayName.ToLowerInvariant().Contains(filter.ToLowerInvariant()); 170 } 171 } 172 }
2.创建TreeViewViewModel,其中定义了用于过滤的属性Filter,以及过滤函数,并在构造函数中初始化一些测试数据,具体代码如下:
1 using System; 2 using System.Collections.Generic; 3 using System.Collections.ObjectModel; 4 using System.ComponentModel; 5 using System.Linq; 6 using System.Text; 7 using System.Threading.Tasks; 8 using System.Windows.Data; 9 10 namespace TreeViewDemo 11 { 12 public class TreeViewViewModel : NotifyPropertyChangedBase 13 { 14 public static TreeViewViewModel Instance = new TreeViewViewModel(); 15 16 private TreeViewViewModel() 17 { 18 Filter = string.Empty; 19 20 Root = new TreeViewItemVM() 21 { 22 Deepth = 0, 23 DisplayName = "五号线", 24 HasChildren = true, 25 Type = NodeType.Unit, 26 ID = "0", 27 Children = new ObservableCollection<TreeViewItemVM>() { 28 new TreeViewItemVM() { DisplayName = "站台", Deepth = 1, HasChildren = true, ID = "1", Type = NodeType.Region, 29 Children = new ObservableCollection<TreeViewItemVM>(){ 30 new TreeViewItemVM() { DisplayName = "Camera 01", Deepth = 2, HasChildren = false, ID = "3",Type = NodeType.Camera }, 31 new TreeViewItemVM() { DisplayName = "Camera 02", Deepth = 2, HasChildren = false, ID = "4",Type = NodeType.Camera }, 32 new TreeViewItemVM() { DisplayName = "Camera 03", Deepth = 2, HasChildren = false, ID = "5",Type = NodeType.Camera }, 33 new TreeViewItemVM() { DisplayName = "Camera 04", Deepth = 2, HasChildren = false, ID = "6",Type = NodeType.Camera }, 34 new TreeViewItemVM() { DisplayName = "Camera 05", Deepth = 2, HasChildren = false, ID = "7", Type = NodeType.Camera}, 35 }}, 36 new TreeViewItemVM() { DisplayName = "进出口", Deepth = 1, HasChildren = true, ID = "10", Type = NodeType.Region, 37 Children = new ObservableCollection<TreeViewItemVM>(){ 38 new TreeViewItemVM() { DisplayName = "Camera 11", Deepth = 2, HasChildren = false, ID = "13",Type = NodeType.Camera }, 39 new TreeViewItemVM() { DisplayName = "Camera 12", Deepth = 2, HasChildren = false, ID = "14",Type = NodeType.Camera }, 40 new TreeViewItemVM() { DisplayName = "Camera 13", Deepth = 2, HasChildren = false, ID = "15",Type = NodeType.Camera }, 41 new TreeViewItemVM() { DisplayName = "Camera 14", Deepth = 2, HasChildren = false, ID = "16", Type = NodeType.Camera}, 42 new TreeViewItemVM() { DisplayName = "Camera 15", Deepth = 2, HasChildren = false, ID = "17", Type = NodeType.Camera}, 43 }}, 44 } 45 }; 46 47 InitTreeView(); 48 } 49 50 private ObservableCollection<TreeViewItemVM> selectedCameras = new ObservableCollection<TreeViewItemVM>(); 51 52 private TreeViewItemVM root; 53 public TreeViewItemVM Root 54 { 55 get 56 { 57 return this.root; 58 } 59 set 60 { 61 if (this.root != value) 62 { 63 this.root = value; 64 this.OnPropertyChanged(() => this.Root); 65 } 66 } 67 } 68 69 /// <summary> 70 /// 过滤字段 71 /// </summary> 72 private string filter; 73 public string Filter 74 { 75 get 76 { 77 return this.filter; 78 } 79 set 80 { 81 if (this.filter != value) 82 { 83 84 this.filter = value; 85 this.OnPropertyChanged(() => this.Filter); 86 87 this.Refresh(); 88 } 89 } 90 } 91 92 /// <summary> 93 /// View 94 /// </summary> 95 protected ICollectionView view; 96 public ICollectionView View 97 { 98 get 99 { 100 return this.view; 101 } 102 set 103 { 104 if (this.view != value) 105 { 106 this.view = value; 107 this.OnPropertyChanged(() => this.View); 108 } 109 } 110 } 111 112 /// <summary> 113 /// 刷新View 114 /// </summary> 115 public void Refresh() 116 { 117 if (this.View != null) 118 { 119 this.View.Refresh(); 120 } 121 } 122 123 private bool DoFilter(Object obj) 124 { 125 TreeViewItemVM item = obj as TreeViewItemVM; 126 if (item == null) 127 { 128 return true; 129 } 130 131 bool result = false; 132 foreach (var node in item.Children) 133 { 134 result = TreeItemDoFilter(node) || result; 135 } 136 137 return result || item.NameContains(this.Filter); 138 } 139 140 private bool TreeItemDoFilter(TreeViewItemVM vm) 141 { 142 if (vm == null) 143 { 144 return true; 145 } 146 147 bool result = false; 148 if (vm.Type == NodeType.Region || vm.Type == NodeType.Unit) 149 { 150 foreach (var item in vm.Children) 151 { 152 result = TreeItemDoFilter(item) || result; 153 } 154 } 155 156 if (result || vm.NameContains(this.Filter)) 157 { 158 result = true; 159 vm.Visible = System.Windows.Visibility.Visible; 160 } 161 else 162 { 163 vm.Visible = System.Windows.Visibility.Collapsed; 164 } 165 166 return result; 167 } 168 169 public void InitTreeView() 170 { 171 this.View = CollectionViewSource.GetDefaultView(this.Root.Children); 172 this.View.Filter = this.DoFilter; 173 this.Refresh(); 174 } 175 } 176 }
3.在界面添加一个TreeView,并添加一个简单的Style,将ViewModel中必要数据进行绑定:
1 <Window x:Class="TreeViewDemo.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="450" Width="525"> 5 <Window.Resources> 6 <Style x:Key="style" TargetType="{x:Type TreeViewItem}"> 7 <Setter Property="Template"> 8 <Setter.Value> 9 <ControlTemplate TargetType="{x:Type TreeViewItem}"> 10 <Grid Visibility="{Binding Visible}" Background="{Binding Background}"> 11 <ContentPresenter ContentSource="Header"/> 12 </Grid> 13 14 <ControlTemplate.Triggers> 15 <Trigger Property="IsSelected" Value="true"> 16 <Setter Property="Background" Value="Green"/> 17 </Trigger> 18 </ControlTemplate.Triggers> 19 </ControlTemplate> 20 </Setter.Value> 21 </Setter> 22 </Style> 23 </Window.Resources> 24 <Grid> 25 <Grid.RowDefinitions> 26 <RowDefinition Height="Auto"/> 27 <RowDefinition Height="*"/> 28 </Grid.RowDefinitions> 29 30 <TextBox x:Name="searchTxt" Width="200" HorizontalAlignment="Center" Height="40" 31 Margin="20" Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> 32 33 <TreeView 34 Grid.Row="1" 35 ItemsSource="{Binding View}"> 36 <TreeView.ItemTemplate> 37 <HierarchicalDataTemplate ItemContainerStyle ="{StaticResource style}" ItemsSource="{Binding Children}"> 38 <Grid Height="25" > 39 <TextBlock 40 x:Name="txt" 41 VerticalAlignment="Center" 42 Text="{Binding DisplayName}" 43 TextTrimming="CharacterEllipsis" 44 ToolTip="{Binding DisplayName}" /> 45 </Grid> 46 </HierarchicalDataTemplate> 47 </TreeView.ItemTemplate> 48 </TreeView> 49 </Grid> 50 </Window>
4.在给界面绑定具体的数据
1 using System.Windows; 2 3 namespace TreeViewDemo 4 { 5 /// <summary> 6 /// MainWindow.xaml 的交互逻辑 7 /// </summary> 8 public partial class MainWindow : Window 9 { 10 public MainWindow() 11 { 12 InitializeComponent(); 13 this.Loaded += MainWindow_Loaded; 14 } 15 16 void MainWindow_Loaded(object sender, RoutedEventArgs e) 17 { 18 this.DataContext = TreeViewViewModel.Instance; 19 } 20 } 21 }
5.运行结果: