with example checkboxes wpf winforms treeview styles
https://tuyentk.googlecode.com/files/TreeViewEx.zip

example - wpf binding treeview



¿Cómo hacer estilo WPF TreeView como WinForms TreeView? (4)

El TreeView predeterminado de WPF es muy bueno, todavía quiero que tenga líneas unidas a cada elemento secundario, como el Windows Forms TreeView. He buscado en Internet y tengo algunos ejemplos, pero no se diseñó lo suficientemente bien.

¿Cómo puedo lograrlo con WPF?


Buen ejemplo El problema en su solución con líneas de puntos es que está utilizando un rectángulo como una línea con un ancho o alto establecido en 1. Si lo hace, los bordes izquierdo y derecho están en el mismo píxel. Esto está bien si estas líneas son sólidas, pero si están punteadas no tienen que tener puntos en los mismos lugares (es decir, el borde izquierdo comienza con puntos en el píxel 0 y el borde derecho en el píxel 1) y este comportamiento hace que sus líneas no es bonito.

La solución es crear líneas de puntos con algo diferente a los rectángulos. Puedes usar por ejemplo Border . Tomé solución desde here .

Cambie las líneas de conexión a:

<!-- 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>


Déjame contestar mi propia pregunta.

Código

Todo lo que necesitas hacer es un archivo XAML y un código detrás:

Primero necesita dibujar Botón Alternar: del botón Triángulo al botón Más-Menos: dibuje un rectángulo con un borde oscuro, luego dibuje dos líneas, una línea vertical y una horizontal. Cuando TreeViewItem se expande, la línea vertical se ocultará:

<!-- 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>

En el código anterior, puede ver un disparador, hará que la línea vertical dentro del botón de alternar se oculte si el elemento está expandido, o mostrará si sus hijos se derrumbaron.

Luego, necesita dibujar líneas de conexión verticales y horizontales entre los nodos: Necesita volver a diseñar TreeViewItem Control. Añade estas líneas de conexión:

<!-- 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"/>

a tu plantilla TreeViewItem como esta:

<!-- 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>

Entonces necesitas poner la clase TreeViewLineConverter en tu espacio de nombres. Esta clase cambiará las líneas de conexión si el elemento es el último en la lista:

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; } } }

Inserte su espacio de nombres en su XAML, es decir:

<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"/>

Agregue esta línea a Window.Resources:

<local:TreeViewLineConverter x:Key="LineConverter"/>

Agregar disparador a la plantilla TreeViewItem, este disparador cambia las líneas de conexión si el elemento es el último en la lista:

<!-- 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>

El TreeView tendrá el estilo de WinForms ahora. Puede agregar más disparador para controlar el comportamiento de TreeView si lo desea. El disparador completo se puede encontrar en el archivo adjunto.

Que hacer

En WinForms TreeView, las líneas de conexión son líneas de puntos. Para hacer estas líneas de puntos, cambie:

<!-- 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"/>

A:

<!-- 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"/>

Pero no es bonito, como ves. Como soy un novato en WPF, no sé cómo diseñar estas líneas a la perfección.

¡Problema!

Hay un problema con la línea vertical cuando agrega un TreeViewItem a TreeView:

Puede sugerirme que cambie el tamaño de la línea vertical, pero si también cambia el tamaño de fuente, no funcionará.

Código fuente

Puedes descargar mi código fuente aquí:
https://tuyentk.googlecode.com/files/TreeViewEx.zip (4.4 KB)

Referencia

Este es el código al que me referí antes de escribir mi propio: MSDN social: Mostrar nodos TreeView conectados con líneas de puntos


Respuesta reelaborada un poco. El tamaño de línea vertical está correlacionado dinámicamente con la altura del elemento y los rectángulos se reemplazan con bordes

<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>


<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>

Solución muy simple, demo de fotos.