一直以来都想对TreeView定义多层模板,并实现数据绑定做一个总结,今天在这里做一个概述,我们常用的两层的TreeView绑定的话,我们首先修改TreeView的模板,这里我们使用的是级联的数据模板,又称之为分层数据模板,这里包含几个重要的属性意义:
DataType指定模板用于哪种数据类型
ItemsSource指定该类数据的子集,即下一层显示那些数据
内容 指定数据如何显示 绑定哪个属性
在我们的例子中第一层模板使用的数据类型是local命名空间下的PlayList类,ItemsSource指定该类数据的子集,即下一层显示那些数据,在这里指的是HierarchicalData Template.ItemTemplate 中的TextBlock使用的数据源类型。此处我们还定义了另外一个类 PlayListItem用来显示第二层数据,即子集显示的类型。这里的Item是我们定义的一个 ObservableCollection<PlayListItem> Item ,有了这个集合我们就能够获取到第二层的数据源,这个是非常重要的。这个集合提供了Add和Remove以及Clear等方法,由于该类实现了INotifyPropertyChanged接口,所以在删除项、增加项或者移除项的时候,会通知UI同步更新数据源,这个是非常重要的,在定义完模板之后,我们就需要将相应的数据添加到数据源中,在主程序中我们将所有的PlayList添加到一个ObservableCollection<PlayList> Root 中,最后我们通过 this.treeView.ItemsSource=Root来实现数据的绑定,至于怎么获取数据源,这个我们可以是获取多级文件夹分别绑定到该类的某一个属性上,或者是从数据库中获取数据然后再添加到数据源中来实现数据的绑定。
<TreeView.ItemTemplate > <HierarchicalDataTemplate DataType="{x:Type local:PlayList}" ItemsSource="{Binding Path=Item}"> <Border BorderBrush="Silver" CornerRadius="3" BorderThickness="0" Margin="3" Name="fathernod" Width="250" ContextMenu="{StaticResource sampleContextMenu1}" MouseRightButtonUp="OnMouseRightButtonUp" > <TextBlock Name="PlayListTextBlock" Text="{Binding Path=GetListName, Mode=TwoWay}" Foreground="White" Margin="0" FontStyle="Normal" FontWeight="Bold"> </TextBlock> </Border> <HierarchicalDataTemplate.ItemTemplate > <DataTemplate> <TextBlock Name="myChildnode" Text="{Binding Path=ResourceName,Mode=OneWay}" Foreground="White" FontSize="18" Margin="-3,2,0,0"> </TextBlock> </DataTemplate> </HierarchicalDataTemplate.ItemTemplate> </HierarchicalDataTemplate> </TreeView.ItemTemplate>
下面再分别贴出两个类的具体代码,仅供参考:
PlayList.cs
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows.Controls; namespace EarthSimulation { public class PlayList : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public PlayList(String name) { this.ListName = name; } public String ListName; //ObservableCollection<T>表示一个动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知。 private ObservableCollection<PlayListItem> _item = new ObservableCollection<PlayListItem>(); public ObservableCollection<PlayListItem> Item { private set { } get { return _item; } } public void AddItem(PlayListItem item1) { _item.Add(item1); } public void RemoveItem(PlayListItem olditem) { _item.Remove(olditem); } public string GetListName { get { return ListName; } set { ListName = value; if (this.PropertyChanged != null)//激发事件,参数为ListName属性 { this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("GetListName")); } } } } }
PlayListItem.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace EarthSimulation { public class PlayListItem { public PlayListItem(PlayList list, int indexTemp) { this.List = list; this.parentIndex = indexTemp; } //获取资源的名称 private string resourcename; public string ResourceName { set { resourcename = value; } get { return resourcename; } } //存储资源的路径 public String DirectoryArray { set; get; } public PlayList List { set; get; } public int parentIndex { set; get; } } }
以上是通过分层模板来定义两层数据,下面介绍如何定义三层数据的数据模板,这里可以参考MSDN上的一个例子:来进行定义,这里是仿照该例子来定义的三层数据模板,这里我们将该模板定义为资源,
<Grid.Resources> <HierarchicalDataTemplate DataType="{x:Type local:PoliceCarRoot}" ItemsSource="{Binding Path=Headers}" > <Border BorderBrush="Silver" CornerRadius="3" BorderThickness="0" Margin="3" Name="fathernod" Width="250" > <StackPanel Orientation="Horizontal" MouseLeftButtonDown="PoliceCarTreeView_MouseLeftButtonDown" > <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsChecked, Mode=TwoWay}"/> <TextBlock Name="GroupTextBlock" Text="{Binding Path=CompanyName, Mode=TwoWay}" Foreground="Black" Margin="1,0,0,0" FontStyle="Normal" FontWeight="Bold" Width="150"> </TextBlock> </StackPanel> </Border> </HierarchicalDataTemplate> <HierarchicalDataTemplate DataType="{x:Type local:PoliceCarGroup}" ItemsSource="{Binding Path=Item}"> <Border BorderBrush="Silver" CornerRadius="3" BorderThickness="0" Margin="3" Name="fathernod" Width="250" > <StackPanel Orientation="Horizontal"> <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsChecked, Mode=TwoWay}" /> <TextBlock Name="PoliceCarTextBlock" Text="{Binding Path=GroupName, Mode=TwoWay}" Foreground="Black" Margin="1,0,0,0" FontStyle="Normal" FontWeight="Bold"> </TextBlock> </StackPanel> </Border> </HierarchicalDataTemplate> <DataTemplate DataType="{x:Type local:PoliceCarInfo}" > <Border BorderBrush="Silver" CornerRadius="3" BorderThickness="0" Margin="3" Name="fathernod" Width="250" > <StackPanel Orientation="Horizontal"> <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsChecked, Mode=TwoWay}" /> <TextBlock Name="PoliceCarTextBlock" Text="{Binding Path=CarNo, Mode=TwoWay}" Foreground="Black" Margin="1,0,0,0" FontStyle="Normal" FontWeight="Bold"> </TextBlock> </StackPanel> </Border> </DataTemplate> </Grid.Resources> <TreeView x:Name="AllPoliceCarListView" Grid.Column="2" HorizontalAlignment="Left" Height="281" Margin="7,17,0,0" Grid.Row="1"
VerticalAlignment="Top" Width="321" Grid.ColumnSpan="2" PreviewMouseRightButtonUp="AllPoliceCarListView_PreviewMouseRightButtonUp"
ContextMenu="{StaticResource sampleContextMenu3}"/>
在TreeView中我们不需要进行相关的模板进行修改,但是在绑定数据源的时候,我们需要将分层的数据绑定到该TreeView中,this.AllPoliceCarListView.ItemsSource = Root,其中Root中是我们添加的三层数据,这样当我们绑定数据源之后,会自动引用我们在资源中定义的三层数据模板,从而为TreeView填充数据,这种情况我们在使用的时候需要特别注意,对于具体怎么定义这些类,如何实现INotifyPropertyChange接口,这个同两层的数据模板绑定时类似,具体参考上面的代码;
总结:如果想定义更多层的数据,只需继续添加分层模板即可,但是也是只能够在资源中进行定义,不能在TreeView.ItemTemplate中定义。另外下面介绍一个加减号的ToggleButton,以及一个比较使用的TreeView样式,具体如下:
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton"> <Setter Property="Focusable" Value="False"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ToggleButton"> <Grid Width="15" Height="13" SnapsToDevicePixels="True"> <!-- Rectangle 9x9 pixels --> <Rectangle Width="15" Height="15" Stroke="#919191" SnapsToDevicePixels="true"> <Rectangle.Fill> <LinearGradientBrush EndPoint="0.5,2" StartPoint="0.5,0"> <GradientStop Color="White" Offset="0"/> <GradientStop Color="Silver" Offset="0.5"/> <GradientStop Color="LightGray" Offset="1"/> </LinearGradientBrush> </Rectangle.Fill> </Rectangle> <!-- 画一个垂直方向的直线 --> <Rectangle x:Name="ExpandPath" Width="2" Height="8" Stroke="Black" SnapsToDevicePixels="true"/> <!-- 画一个水平方向的直线 --> <Rectangle Width="8" Height="2" Stroke="Black" SnapsToDevicePixels="true"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="True"> <Setter Property="Visibility" TargetName="ExpandPath" Value="Collapsed"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type TreeViewItem}"> <Setter Property="Background" Value="Transparent"/> <!--此处设置TreeViewItem的绑定样式--> <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"></Setter> <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> <Setter Property="Padding" Value="1,0,0,0"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type TreeViewItem}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition MinWidth="19" Width="Auto"/> <ColumnDefinition Width="150" MinWidth="150"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" MinHeight="22"/> <RowDefinition /> </Grid.RowDefinitions> <!-- Connecting Lines --> <!-- Horizontal line --> <Rectangle x:Name="HorLn" Margin="9,1,0,0" Height="1" Stroke="Gray" SnapsToDevicePixels="True"/> <!-- Vertical line --> <Rectangle x:Name="VerLn" Width="1" Stroke="Gray" Margin="0,0,1,0" Grid.RowSpan="2" SnapsToDevicePixels="true" Fill="White"/> <ToggleButton x:Name="Expander" Grid.Column="0" Grid.Row="0" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/> <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true"> <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Width="250"/> </Border> <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1" /> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsExpanded" Value="false"> <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/> </Trigger> <Trigger Property="HasItems" Value="false"> <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/> </Trigger> <Trigger Property="IsSelected" Value="true"> <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/> </Trigger> <!--<MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsSelected" Value="true"/> <Condition Property="IsSelectionActive" Value="false"/> </MultiTrigger.Conditions> <Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> </MultiTrigger>--> <Trigger Property="IsEnabled" Value="false"> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="VirtualizingStackPanel.IsVirtualizing" Value="true"> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <VirtualizingStackPanel/> </ItemsPanelTemplate> </Setter.Value> </Setter> </Trigger> </Style.Triggers> </Style>