項目中有一個樹形結構的資源,需要支援搜尋功能,搜尋出來的結果還是需要按照樹形結構展示,下面是簡單實作的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.運作結果: