如何将 WPF TreeView 样式设置为 WinForms TreeView?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19560466/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to make WPF TreeView style as WinForms TreeView?
提问by Tuyen Pham
WPF default TreeView is very good, I still want it have lines joint each its child element, like Windows Forms TreeView. I have searched on internet and have some example, but it did not designed well enough.
WPF 默认的 TreeView 非常好,我仍然希望它的每个子元素都有线连接,就像 Windows 窗体 TreeView。我在互联网上搜索过并有一些例子,但它设计得不够好。
How can I achieve it with WPF?
我怎样才能用 WPF 实现它?
回答by Tuyen Pham
Let me answer my own question.
让我回答我自己的问题。


Code
代码
All you need to do is a XAML file and a code behind:
您需要做的只是一个 XAML 文件和背后的代码:
First you need draw Toggle Button: From Triangle button to Plus-Minus button: draw a rectangle with dark border, then draw two lines, one vertical line and one horizontal line. When TreeViewItem is expanded, the vertical line will hide:
首先你需要画切换按钮:从三角形按钮到加减按钮:画一个黑色边框的矩形,然后画两条线,一条垂直线,一条水平线。当 TreeViewItem 展开时,垂直线将隐藏:
<!-- Toggle Button -->
<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="9" Height="9" 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>
<!-- Vertical line inside rectangle -->
<Rectangle x:Name="ExpandPath" Width="1" Height="5" Stroke="Black" SnapsToDevicePixels="true"/>
<!-- Horizontal line inside rectangle -->
<Rectangle Width="5" Height="1" 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>
In above code, you can see a trigger, it will make the vertical line inside toggle button hide if item is expand, or show if its children collapsed.
在上面的代码中,您可以看到一个触发器,它会在项目展开时隐藏切换按钮内的垂直线,或者显示其子项是否折叠。
Then, you need draw vertical and horizontal connecting lines between nodes: You need redesign TreeViewItem Control. Add these connecting lines:
然后,您需要在节点之间绘制垂直和水平连接线: 您需要重新设计 TreeViewItem 控件。添加这些连接线:
<!-- Horizontal line -->
<Rectangle x:Name="HorLn" Margin="9,1,0,0" Height="1" Stroke="#DCDCDC" SnapsToDevicePixels="True"/>
<!-- Vertical line -->
<Rectangle x:Name="VerLn" Width="1" Stroke="#DCDCDC" Margin="0,0,1,0" Grid.RowSpan="2" SnapsToDevicePixels="true" Fill="White"/>
to your TreeViewItem template like this:
到您的 TreeViewItem 模板,如下所示:
<!-- TreeViewItem -->
<Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="19" Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Connecting Lines -->
<!-- Horizontal line -->
<Rectangle x:Name="HorLn" Margin="9,1,0,0" Height="1" Stroke="#DCDCDC" SnapsToDevicePixels="True"/>
<!-- Vertical line -->
<Rectangle x:Name="VerLn" Width="1" Stroke="#DCDCDC" Margin="0,0,1,0" Grid.RowSpan="2" SnapsToDevicePixels="true" Fill="White"/>
<!-- Insert Toggle Button -->
<ToggleButton Margin="-1,0,0,0" x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"/>
<Border Name="Bd" Grid.Column="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" MinWidth="20"/>
</Border>
<ItemsPresenter x:Name="ItemsHost" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then you need put the class TreeViewLineConverter to your namespace. This Class will changes the connecting lines if the item is the last in the list:
然后您需要将类 TreeViewLineConverter 放入您的命名空间。如果项目是列表中的最后一项,此类将更改连接线:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace TreeViewEx
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
class TreeViewLineConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
TreeViewItem item = (TreeViewItem)value;
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(item);
return ic.ItemContainerGenerator.IndexFromContainer(item) == ic.Items.Count - 1;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return false;
}
}
}
Insert your namespace to your XAML, ie:
将您的命名空间插入您的 XAML,即:
<Window x:Class="TreeViewEx.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewEx"/>
Add this line to Window.Resources:
将此行添加到 Window.Resources:
<local:TreeViewLineConverter x:Key="LineConverter"/>
Add trigger to TreeViewItem template, this trigger changes the connecting lines if the item is the last in the list:
向 TreeViewItem 模板添加触发器,如果项目是列表中的最后一个,则此触发器更改连接线:
<!-- This trigger changes the connecting lines if the item is the last in the list -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource LineConverter}}" Value="true">
<Setter TargetName="VerLn" Property="Height" Value="9"/>
<Setter TargetName="VerLn" Property="VerticalAlignment" Value="Top"/>
</DataTrigger>
The TreeView will have WinForms style now. You can add more trigger to control behavie of TreeView if you want. The full trigger can found on attached file.
TreeView 现在将具有 WinForms 样式。如果需要,您可以添加更多触发器来控制 TreeView 的行为。完整的触发器可以在附件中找到。
ToDo
去做
In WinForms TreeView, the connecting lines is a dotted lines. To make these lines dotted, change:
在 WinForms TreeView 中,连接线是虚线。要使这些线成为虚线,请更改:
<!-- Connecting Lines -->
<Rectangle x:Name="HorLn" Margin="9,1,0,0" Height="1" Stroke="#DCDCDC" SnapsToDevicePixels="True"/>
<Rectangle x:Name="VerLn" Width="1" Stroke="#DCDCDC" Margin="0,0,1,0" Grid.RowSpan="2" SnapsToDevicePixels="true" Fill="White"/>
To:
到:
<!-- Connecting Lines -->
<Rectangle x:Name="HorLn" Margin="9,1,0,0" Height="1" Stroke="Blue" StrokeDashCap="Square" StrokeDashArray="0,2" StrokeDashOffset="1" SnapsToDevicePixels="True"/>
<Rectangle x:Name="VerLn" Width="1" Stroke="Blue" StrokeDashCap="Square" StrokeDashArray="0,2" Margin="0,0,1,0" Grid.RowSpan="2" SnapsToDevicePixels="true" Fill="White"/>


But it is not pretty, as you see. As I'm a newbie in WPF, I don't know to style these line perfectly.
但正如你所见,它并不漂亮。由于我是 WPF 的新手,我不知道如何完美地设计这些线条。
Problem!
问题!
There is a problem with vertical line when you add a TreeViewItem into TreeView:
在TreeView中添加TreeViewItem时出现竖线问题:


You may suggest me change Vertical Line size, but if you change the font size, too, it will not work.
您可能会建议我更改垂直线的大小,但是如果您也更改字体大小,它将不起作用。
Source code
源代码
You can download my source code here:
https://tuyentk.googlecode.com/files/TreeViewEx.zip(4.4 KB)
你可以在这里下载我的源代码:
https: //tuyentk.googlecode.com/files/TreeViewEx.zip(4.4 KB)
Reference
参考
This is the code I refereced before I wrote my own: Social MSDN: Show TreeView nodes connected with dotted lines
这是我在编写自己的代码之前参考的代码:Social MSDN: Show TreeView nodes connected with dotted lines
回答by Artholl
Nice example. Problem in your solution with dotted lines is that you are using rectangle as a line with width or height set to 1. If you do that, than left and right borders are on the same pixel. This is fine if these lines are solid, but if they are dotted they don't have to have dots on the same places (ie. left border starts with dots at pixel 0 and right border at pixel 1) and this behaviour makes your lines not pretty.
很好的例子。虚线解决方案中的问题是您将矩形用作宽度或高度设置为 1 的线。如果这样做,则左右边框位于同一像素上。如果这些线是实线,这很好,但如果它们是虚线,则它们不必在相同的位置有点(即左边框以像素 0 处的点开始,右边框处像素 1 处的点开始)并且这种行为使您的线条不漂亮。
Solution is to create dotted lines with something different than rectangles. You can use for example Border. I took solution from here.
解决方案是创建与矩形不同的虚线。例如,您可以使用Border. 我从这里采取了解决方案。
Change connecting lines to:
将连接线更改为:
<!-- Connecting Lines -->
<Border x:Name="HorLn" Margin="9,0,0,0" HorizontalAlignment="Stretch" Height="1" BorderThickness="0,0,0,1">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="2,0" SpreadMethod="Repeat" MappingMode="Absolute">
<GradientStop Color="Transparent" Offset="0" />
<GradientStop Color="Transparent" Offset="0.499" />
<GradientStop Color="#999" Offset="0.5" />
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
<Border x:Name="VerLn" Margin="0,0,1,0" Grid.RowSpan="2" VerticalAlignment="Stretch" Width="1" BorderThickness="0,0,1,0">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,2" SpreadMethod="Repeat" MappingMode="Absolute">
<GradientStop Color="Transparent" Offset="0" />
<GradientStop Color="Transparent" Offset="0.499" />
<GradientStop Color="#999" Offset="0.5" />
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
回答by mkonvisar
Answer reworked a bit. Vertical line size is dynamic correlated with item height, and rectangles replaced with borders
答案稍微修改了一下。垂直线大小与项目高度动态相关,矩形替换为边框
<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 Width="9" Height="9" Stroke="#919191" SnapsToDevicePixels="true" Fill="White"/>
<Rectangle x:Name="ExpandPath" Width="1" Height="5" Stroke="Black" SnapsToDevicePixels="true"/>
<Rectangle Width="5" Height="1" 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 x:Key="TreeViewStyle" TargetType="{x:Type TreeViewItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Padding" Value="0,0,0,0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TreeViewItem}">
<Grid Name="ItemRoot">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Name="Lines" Grid.Column="0" Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="1" BorderThickness="1 0 0 1" SnapsToDevicePixels="True" BorderBrush="{TemplateBinding BorderBrush}"/>
<Border Grid.Row="1" Grid.Column="1" BorderThickness="1 0 0 0" SnapsToDevicePixels="True" BorderBrush="{TemplateBinding BorderBrush}" Name="LineToNextItem"
Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type TreeViewItem}},
Converter={StaticResource LineConverter}}"/>
</Grid>
<ToggleButton x:Name="Expander" Grid.Column="0" Grid.Row="0"
Style="{StaticResource ExpandCollapseToggleStyle}"
IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
ClickMode="Press"/>
<Border Name="Bd" Grid.Column="1" Grid.Row="0"
HorizontalAlignment="Left"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" MinWidth="20"/>
</Border>
<Grid Grid.Column="0" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Border Grid.Column="1" BorderThickness="1 0 0 0" SnapsToDevicePixels="True" BorderBrush="{TemplateBinding BorderBrush}"
Visibility="{Binding ElementName=LineToNextItem, Path=Visibility}"/>
</Grid>
<ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.Row="1" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="IsExpanded" Value="false">
<Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
</Trigger>
<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>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
回答by Thanh Bao
<TreeView Name="TreeView" Margin="24">
<Border CornerRadius="20" BorderBrush="Red" BorderThickness="1 0 0 0">
<TreeViewItem Header="aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IsExpanded="True">
<Border CornerRadius="20" BorderBrush="Red" BorderThickness="1 0 0 0">
<TreeViewItem Header="aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IsExpanded="True">
<Border CornerRadius="20" BorderBrush="Red" BorderThickness="1 0 0 0">
<TreeViewItem Header="aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IsExpanded="True">
<Border CornerRadius="20" BorderBrush="Red" BorderThickness="1 0 0 0">
<TreeViewItem Header="aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IsExpanded="True">
</TreeViewItem>
</Border>
</TreeViewItem>
</Border>
<Border CornerRadius="20" BorderBrush="Red" BorderThickness="1 0 0 0">
<TreeViewItem Header="aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IsExpanded="True">
</TreeViewItem>
</Border>
</TreeViewItem>
</Border>
<Border CornerRadius="20" BorderBrush="Red" BorderThickness="1 0 0 0">
<TreeViewItem Header="aaaaaaaaaaaaaaaaaaaaaaaaaaaaa" IsExpanded="True">
</TreeViewItem>
</Border>
</TreeViewItem>
</Border>
</TreeView>

