Одним из часто используемых элементов управления является дерево, оснащенное дополнительными колонками для детального представления элементов.
Библиотека WPF, на первый взгляд, не содержит такого элемента. ;-)
Но существует несколько сторонних разработок, позволяющих добиться такого эффекта.
-
1. Telerik RadTreeListView. Элемент управления. созданный компанией Telerik, не имеющий ничего общего с System.Windows.Controls.TreeView и напрямую наследующий System.Windows.Controls.ItemsControl. Прямо из коробки этот элемент не содержит колонок. Но после небольшой доработки, предложенной автором(
http://www.telerik.com/community/forums/wpf/treeview/multicolumn-treeview.aspx) этот элемент превращается в довольно симпатичное дерево с колонками. Колонки задаются довольно просто.
-
CodeProject: TreeListView (
http://www.codeproject.com/KB/WPF/TreeListView.aspx) Это основанный на System.Windows.Controls.TreeView элемент управления который содержит несколько десятков строк кода на С#, вместе с замысловатым в котором вычисляют насколько необходимо сдвинуть строку от левого края в зависимости от уровня элемента дерева. Через этот конвертор затем выполняют привязку ширины фиктивного элемента осуществляющего сдвиг. Кроме того элемент переопределяет почти все шаблоны из TreeView. Неплохой вариант, поддерживает даже изменения порядка колонок, но слишком уж сложно все организовано.
-
Mindscape MulticolumnTreeView (
http://www.mindscape.co.nz/) Показывает колонки прямо из коробки, никаких доработок не требует. Но опять-таки стоит денег.
Тут я предлагаю вспомнить о том, что элементы управления в WPF не имеют жестко заданного внешнего вид, а имеют только поведение и дерево это нечто, что содержит иерархию элементов, а как выглядеть этим элементам решать разработчику. Для того, чтобы добавить колонки в обычное дерево мы переопределим его ItemTemplate:
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Children}">
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="1"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Path=Title}" Grid.Column="0" />
<TextBlock Text="{Binding Path=DateTime}" Grid.Column="1"
Width="{Binding ElementName=DateTimeHeader, Path=ActualWidth}"/>
<TextBlock Text="{Binding Path=Raiting}" Grid.Column="2"
Width="{Binding ElementName=RaitingHeader, Path=ActualWidth}"/>
<Rectangle Width="{Binding ElementName=EmptyHeader, Path=ActualWidth}" Grid.Column="3" />
</Grid>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
Затем добавим заголовок, чтобы отображать названия колонок и изменять их размер.
<Control.Template>
<ControlTemplate>
<StackPanel>
<Grid x:Name="HeaderGrid">
<Grid.Resources>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Margin" Value="1"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<GridViewColumnHeader Content="Title" Grid.Column="0" x:Name="TitleHeader" Width="220" />
<GridViewColumnHeader Content="Date/Time" Grid.Column="1" x:Name="DateTimeHeader" />
<GridViewColumnHeader Content="Raiting" Grid.Column="2" x:Name="RaitingHeader" />
<Rectangle Grid.Column="3" x:Name="EmptyHeader" />
</Grid>
<ItemsPresenter Width="{Binding ElementName=HeaderGrid, Path=ActualWidth}" />
</StackPanel>
</ControlTemplate>
</Control.Template>
В этом коде на XAML есть одна небольшая хитрость: в заголовке последняя колонка элемента Grid заполняет все оставшееся место, а в шаблоне элемента дерева первая (ведь у каждого элемента она разная, т.к. элементы разных уровней по-разному отодвинуты от левого края). Кроме того добавлен прямоугольник заполняющий свободную область справа и выполнены соответствующие привязки ширины элементов и данных в элементах TextBlock. На этом казалось бы можно остановится, но шаблон элемента TreeViewItem из которого строится дерево не растягивает свое содержимое на все доступное пространство. Поэтому его придется переопределить. Возьмем его стандартный шаблон и изменим его всего в двух местах.
В единственном элементе Grid вместо
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
оставляем только две колонки
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
И в элементе ContentPresenter устанавливаем HorizontalAlignment="Stretch".
Добавим мелкие изменения, связанные с удалением одной колонки. Не забываем также вставить привязку
IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource Mode=TemplatedParent, AncestorType={x:Null}}}"
в элемент TogleButton. Она почему-то не видна в шаблоне, если его извлекать при помощи XamlWriter и свойства Template соответствующего элемента управления.
Вот и все наш элемент готов и без единой строчки Code-Behind.
Полный код примера здесь: DetailedTree или здесь: http://cid-c22b7611bb9d0310.skydrive.live.com/browse.aspx/Samples