TreeView In WPF
涉及WPF相关知识点
DependencyProperty、Command、MarkupExtension、DataBind、Style、Template、 Resource、Trigger
详细细节参考相关技术文章
Example 介绍
功能
文本框中输入字符模糊查找树中的节点,如果匹配则自动展开树并高亮匹配节点。
文件结构说明
Model文件夹下是持久化数据对象(PO),例子中使用MockDatabase来模拟数据库持久化。
ViewModel文件夹下是现实数据对象(VO),用来和WPF控件进行数据绑定。
关键点说明
TreeView
base.DataContext = GroupTreeViewModel.Instance;
MainWindow.xaml.cs文件中为整个MainWindow绑定一个数据源
<TreeView ItemsSource="{Binding Path=Groups}">
TreeView和GroupTreeViewModel中的Groups属性进行绑定,是一次性单项绑定(即在程序启动时把数据往界面上绑定一次,不带界面数据同步)。
<TreeView.ItemContainerStyle> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" /> <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" /> <Setter Property="FontWeight" Value="Normal" /> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="Bold" /> </Trigger> </Style.Triggers> </Style> </TreeView.ItemContainerStyle>
为TreeView设置样式,其中Property="IsExpanded"、Property="IsSelected"、 Property="FontWeight"都是TreeViewItem的控件属性。
{Binding IsExpanded, Mode=TwoWay},这里的IsExpanded是RoomVO或者GroupVO的属性(不是GroupTreeViewModel的属性,因为通过TreeView自身的绑定之后数据源发生了变化,TreeViewItem是TreeView的子节点,在WPF中子节点是继承父节点的数据源的,就像TreeView继承Window的数据源一样)
Mode=TwoWay,表示界面和数据双向绑定(即TreeViewItem.IsExpanded和 GroupVO.IsExpanded、RoomVO.IsExpanded做同步变更)
标记扩展{Binding ...}用于为目标对象进行数据绑定,目标对象必须是一个依赖属性;源对象则可以是一个普通的属性。如果源对象发生数据变化需要通知目标对象进行同步,则绑定的数据源类型必须实现INotifyPropertyChanged接口。也就是说只填写Mode=TwoWay,不实现 INotifyPropertyChanged接口是无法完成目标数据和源数据的双向同步的。
this.FirePropertyChanged("IsSelected"); private void FirePropertyChanged(string propertyName) { if(this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
例子中的GroupVO.cs和RoomVO.cs里都有如下代码,用来完成TreeViewItem和GroupVO、RoomVO中的 IsSelected属性值的双向同步绑定
<TreeView.Resources> <HierarchicalDataTemplate DataType="{x:Type m:GroupVO}" ItemsSource="{Binding Rooms}"> <StackPanel Orientation="Horizontal"> <Image Width="16" Height="16" Margin="3,0" Source="Images\GroupIcon.png" /> <TextBlock Text="{Binding FriendlyName}" /> </StackPanel> </HierarchicalDataTemplate> <DataTemplate DataType="{x:Type m:RoomVO}"> <StackPanel Orientation="Horizontal"> <Image Width="16" Height="16" Margin="3,0" Source="Images\RoomIcon.png" /> <TextBlock Text="{Binding FriendlyName}" /> </StackPanel> </DataTemplate> </TreeView.Resources>
为TreeView定义模板,用来表述树中各各节点和数据的绑定关系。HierarchicalDataTemplate是有层级结构的模板,DataTemplate是普通数据模板。
DataType="{x:Type m:GroupVO}"指定HierarchicalDataTemplate的数据类型,DataType="{x:Type m:RoomVO}指定DataTemplate的数据类型,由于TreeView同时定义了两个数据模板,而数据源只有一个(GroupTreeViewModel),WPF迷茫了,不知道怎么绑定数据,所以需要为每个模板写明数据类型。当然如果只有一个模板的情况下,可以忽略DataType,WPF会自动识别。
x和m,都是命名空间的缩写,在MainWindow.xaml的头上都做了定义
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:m="clr-namespace:TestTreeView.ViewModel"
x:Type,Type是WPF的关键字
m:GroupVO,就是TestTreeView.ViewModel.GroupVO的类型
Search
<TextBox x:Name="TextBoxSearch" KeyDown="TextBoxSearch_KeyDown" Margin="0,0,4,0" Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" Width="150" Height="21" /> <Button Command="{Binding m:SearchCommand}" Content="搜索" Padding="8,0" Height="22" Width="50" Click="SearchButton_Click" />
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
当TextBox.Text属性发生变化,更新数据源SearchText
x:Name="TextBoxSearch"
定义了一个TextBox TextBoxSearch变量,在后台代码可以访问
Command="{Binding m:SearchCommand}"
将Button的点击事件和一个ICommand绑定,Command在GroupTreeViewModel.cs中定义如下:
private class SearchTreeCommand : ICommand { readonly GroupTreeViewModel model; public SearchTreeCommand(GroupTreeViewModel model) { this.model = model; } public bool CanExecute(object parameter) { return true; } event EventHandler ICommand.CanExecuteChanged { add { } remove { } } public void Execute(object parameter) { model.Search(); } }
代码:Code