Заголовок TreeViewItem не растягивается?
Эта проблема возникает из-за того, что шаблон WPF по умолчанию для TreeViewItem
настроен как 3-столбец для 2-строки Grid
. Первая строка для «заголовка» (на самом деле Border
), а вторая строка для ItemsPresenter
. Две строки становятся видимыми или скрытыми по мере необходимости, чтобы выполнить расширение дерева, когда вы щелкаете по маленькому треугольнику - который занимает нулевой столбец Grid
.
Обе строки действительно нуждаются только в одном дополнительном столбце. Например, во второй строке у нас ничего не должно быть в col-0, row-1, потому что эта пустая часть должна иметь отступ, если IsExpanded
имеет значение true. Но загадка начинается, когда мы отмечаем, что ItemsPresenter
, основанный на столбце col-1, row-1, указывает Grid.ColumnSpan=2
.
К сожалению, в верхнем ряду Border
, который содержит заголовок, установлен на Grid.Column=1
... но без ColumnSpan. Поскольку у col-2 Grid
есть Width=*
, это означает, что заголовок / рамка не будут растягиваться горизонтально.
Другими словами, мне кажется, что у сетки с тремя столбцами нет никакой цели, кроме как специально предотвратить растяжение заголовка. Насколько я могу судить, простое расположение 2x2 было бы более гибким [править: см. Сноску # 2] и поддерживало бы либо полное растяжение , либо не растягивающийся заголовок с "зубчатыми" заголовками через обычный 1027 * выравнивающие механизмы.
В идеале, мы бы изменили Grid
, чтобы иметь только 2 столбца вместо 3. Так как это не так просто, вместо этого мы сделаем заголовок перекрывающим 2 столбца так же, как ItemsPresenter
.
Хорошо, вот небольшая, полная, автономная (только для XAML) рабочая программа , которая демонстрирует и исправляет проблему:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/netfx/2007/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:r="clr-namespace:System.Reflection;assembly=mscorlib"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Width="800" SizeToContent="Manual">
<TreeView ItemsSource="{Binding Source={StaticResource data}}"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingPanel.ScrollUnit="Item">
<TreeView.Resources>
<ObjectDataProvider x:Key="data" ObjectInstance="{x:Static sys:AppDomain.CurrentDomain}" MethodName="GetAssemblies" />
<HierarchicalDataTemplate DataType="{x:Type r:Assembly}" ItemsSource="{Binding Path=DefinedTypes}" >
<TextBlock Text="{Binding Path=Location}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type sys:Type}" ItemsSource="{Binding Path=CustomAttributes}">
<TextBlock Text="{Binding Path=Name}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type r:CustomAttributeData}" ItemsSource="{Binding Path=ConstructorArguments}">
<TextBlock Text="{Binding Path=AttributeType.Name}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<!-- == == BEGIN HERE == == -->
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Grid.ColumnSpan" Value="2" />
</Style>
</Style.Resources>
<!-- == == == END == == == -->
<Setter Property="Background" Value="LightBlue" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Window>
Если вы запустите эту программу, как показано, вы увидите что-то вроде этого. Это фиксированное поведение, которое позволяет восстановить полный контроль над поведением растяжения заголовка TreeViewItem
:
Обратите внимание на деталь BEGIN / END с пунктирными линиями в источнике XAML. По сути, я просто установил Grid.ColumnSpan=2
на неправильный Border
, чтобы он заполнил растянутую ширину Grid
. Этот элемент испускается шаблоном TreeViewItem
, поэтому я обнаружил, что эффективный способ изменить его свойства - это настроить таргетинг Style
в словаре ресурсов из TreeViewItem
Style
. Да, сбивает с толку. Доступ к Style
осуществляется через TreeViewItem.ItemContainerStyle
.
Чтобы увидеть (существующее) прерывистое поведение, вы можете закомментировать часть между пунктирными линиями:
Вы также можете установить эти стили в некотором словаре ресурсов вместо использования свойства ItemContainerStyle
, как я это сделал здесь. Я сделал это так, потому что это сводит к минимуму объем исправления, так что не связанные элементы управления Border
не будут затронуты. Если вам нужен более дискриминационный способ нацеливания именно на этот элемент управления, вы можете воспользоваться тем, что он имеет Name='Bd'
.
[edit:] Это решение не использует отражение! Не пугайтесь бессмысленных демонстрационных данных - оно не имеет никакого отношения к этой проблеме; это был просто самый простой способ получить некоторые иерархические данные для демонстрационных целей, сохранив при этом всю программу крошечной.
[edit # 2:] Я только что понял, что дизайнеры пытались избежать с помощью сетки 3х2, это был следующий неприглядный эффект (преувеличенный здесь уменьшенным скриншотом). Поэтому, если вы воспользуетесь одним из решений на этой странице, имейте в виду, что вы можете не захотеть этого: