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>

 

posted @ 2015-01-26 22:53  Jerry Tong  阅读(5276)  评论(1编辑  收藏  举报