WPF - TreeView 仿VS2013解决方案资源管理器中的树状结构
效果图
先上效果图,若是你想要的效果,可以继续看下面的代码,不想浪费大家的时间。
样式定义
此处定义TreeView的样式,参考自MSDN,稍作修改。
注意:在TreeViewItem控件模板定义中绑定一个数据(Level)以及一个值转换器(LevelToMarginConverter),具体定义见下部分。
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton"> <Setter Property="Focusable" Value="False"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ToggleButton"> <Border Width="12" Height="14"> <Path x:Name="ExpandPath" HorizontalAlignment="Right" VerticalAlignment="Center" Fill="Transparent" Stroke="#FFE6E6E6" Data="M 2 2 L 7 7 L 2 12 Z"/> </Border> <ControlTemplate.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsMouseOver" Value="True"/> <Condition Property="IsChecked" Value="False"/> </MultiTrigger.Conditions> <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/> </MultiTrigger> <Trigger Property="IsChecked" Value="True"> <Setter Property="Data" TargetName="ExpandPath" Value="M 1 10 L 7 3 L 7 10 Z"/> <Setter Property="Fill" TargetName="ExpandPath" Value="#FFE6E6E6"/> </Trigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="IsMouseOver" Value="True"/> <Condition Property="IsChecked" Value="True"/> </MultiTrigger.Conditions> <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/> <Setter Property="Fill" TargetName="ExpandPath" Value="#FF1BBBFA"/> </MultiTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="TreeViewItemFocusVisual"> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Border> <Rectangle Margin="0,0,0,0" StrokeThickness="5" Stroke="Black" StrokeDashArray="1 2" Opacity="0" Fill="Black"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="ModernTreeViewItem" TargetType="{x:Type TreeViewItem}"> <Setter Property="Background" Value="Transparent"/> <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/> <Setter Property="VerticalContentAlignment" Value="{Binding Path=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.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition /> </Grid.RowDefinitions> <Border Name="Bd" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"> <Grid Margin="{Binding Level, Converter={StaticResource LevelToMarginConverter}}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" ClickMode="Press" HorizontalAlignment="Left" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"/> <ContentPresenter Grid.Column="1" Margin="8,0,0,0" x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"/> </Grid> </Border> <ItemsPresenter x:Name="ItemsHost" Grid.Row="1"/> </Grid> <ControlTemplate.Triggers> <Trigger Property="IsExpanded" Value="false"> <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/> </Trigger> <Trigger Property="HasItems" Value="false"> <Setter TargetName="Expander" Property="Visibility" Value="Hidden"/> </Trigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="HasHeader" Value="false"/> <Condition Property="Width" Value="Auto"/> </MultiTrigger.Conditions> <Setter TargetName="PART_Header" Property="MinWidth" Value="75"/> </MultiTrigger> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="HasHeader" Value="false"/> <Condition Property="Height" Value="Auto"/> </MultiTrigger.Conditions> <Setter TargetName="PART_Header" Property="MinHeight" Value="19"/> </MultiTrigger> <Trigger Property="IsSelected" Value="true"> <Setter TargetName="Bd" Property="Background" 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 TargetName="Bd" Property="Background" 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>
值转换器定义
此转换器主要用于根据子节点的级数计算每行内容的缩进,根节点级数为1,根节点的子节点级数为2,依次类推。
class LevelToMarginConverter : System.Windows.Data.IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { var level = (int)value; return new System.Windows.Thickness(8 * level + 10 * (level - 1), 0, 0, 0); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return null; } }
数据定义
此处定义的TreeView空间绑定的数据。
注意:在TreeNode类中定义了Level属性,用于指示当前节点的级数。
public class TreeViewData { private static TreeViewData _Data = null; public static TreeViewData Data { get { if(_Data == null) { _Data = new TreeViewData(); var rn1 = new TreeNode() { Label = "Root A", Level = 1 }; rn1.ChildNodes.Add(new TreeNode() { Label = "Root A - Child 1", Level = 2 }); rn1.ChildNodes.Add(new TreeNode() { Label = "Root A - Child 2", Level = 2 }); rn1.ChildNodes.Add(new TreeNode() { Label = "Root A - Child 3", Level = 2 }); rn1.ChildNodes.Add(new TreeNode() { Label = "Root A - Child 4", Level = 2 }); rn1.ChildNodes.Add(new TreeNode() { Label = "Root A - Child 5", Level = 2 }); var rn2 = new TreeNode() { Label = "Root B", Level = 1 }; rn2.ChildNodes.Add(new TreeNode() { Label = "Root B - Child 1", Level = 2 }); var rn21 = new TreeNode() { Label = "Root B - Child 2", Level = 2 }; rn21.ChildNodes.Add(new TreeNode() { Label = "Root B - Child 2 - Child 1", Level = 3 }); rn21.ChildNodes.Add(new TreeNode() { Label = "Root B - Child 2 - Child 2", Level = 3 }); rn2.ChildNodes.Add(rn21); rn2.ChildNodes.Add(new TreeNode() { Label = "Root B - Child 3", Level = 2 }); rn2.ChildNodes.Add(new TreeNode() { Label = "Root B - Child 4", Level = 2 }); rn2.ChildNodes.Add(new TreeNode() { Label = "Root B - Child 5", Level = 2 }); var rn3 = new TreeNode() { Label = "Root C", Level = 1 }; rn3.ChildNodes.Add(new TreeNode() { Label = "Root C - Child 1", Level = 2 }); rn3.ChildNodes.Add(new TreeNode() { Label = "Root C - Child 2", Level = 2 }); rn3.ChildNodes.Add(new TreeNode() { Label = "Root C - Child 3", Level = 2 }); rn3.ChildNodes.Add(new TreeNode() { Label = "Root C - Child 4", Level = 2 }); rn3.ChildNodes.Add(new TreeNode() { Label = "Root C - Child 5", Level = 2 }); _Data.RootNodes.Add(rn1); _Data.RootNodes.Add(rn2); _Data.RootNodes.Add(rn3); } return _Data; } } private System.Collections.ObjectModel.ObservableCollection<TreeNode> _RootNodes = null; public IList<TreeNode> RootNodes { get { return _RootNodes ?? (_RootNodes = new System.Collections.ObjectModel.ObservableCollection<TreeNode>()); } } public class TreeNode { public string Label { get; set; } public int Level { get; set; } private System.Collections.ObjectModel.ObservableCollection<TreeNode> _ChildNodes = null; public IList<TreeNode> ChildNodes { get { return _ChildNodes ?? (_ChildNodes = new System.Collections.ObjectModel.ObservableCollection<TreeNode>()); } } } }
应用样式
<TreeView ItemsSource="{Binding Source={x:Static data:TreeViewData.Data}, Path=RootNodes}" HorizontalContentAlignment="Stretch" Background="#FF252526" Width="300" ItemContainerStyle="{StaticResource ModernTreeViewItem}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type data:TreeViewData+TreeNode}" ItemsSource="{Binding ChildNodes}"> <Grid Height="32"> <TextBlock Text="{Binding Label}" VerticalAlignment="Center" Foreground="#FFE6E6E6" FontSize="13"/> </Grid> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView>