как получить доступную (видимую) ширину TreeViewItem внутри TreeView - PullRequest
1 голос
/ 13 июля 2010

У меня есть TreeViewItem s, где HierarchicalDataTemplate состоит из сетки с 3 столбцами со следующими определениями:

<Grid>    
  <Grid.ColumnDefinitions>
    <ColumnDefinition Width="Auto" />
    <ColumnDefinition Width="1*" />
    <ColumnDefinition Width="Auto" />
...

Я хочу установить ширину сетки так, чтобы она занимала ровно все доступное пространство TreeViewItem внутри TreeView. Поэтому третий столбец сетки должен быть выровнен по правому краю внутри TreeView.

Как получить правильное значение ширины сетки?

Я знаю, что для ListBox ItemTemplate я могу установить ширину, привязав ее к ScrollContentPresenter:

Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ScrollContentPresenter, AncestorLevel=1}, Path=ActualWidth}"

Этот прием не работает в TreeView, поскольку у детей меньше места, чем у корневых элементов дерева.

Есть идеи?

Ответы [ 3 ]

6 голосов
/ 15 июля 2010

Проработав несколько часов, я нашел рабочее решение, использующее Multibinding и два конвертера .

Во-первых, определение HierarchicalDataTemplate в XAML:

<HierarchicalDataTemplate>
<Grid>
   <Grid.Width>
      <MultiBinding Converter="{StaticResource SumConverterInstance}">
          <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=ScrollContentPresenter, AncestorLevel=1}" Path="ActualWidth" />
          <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType=TreeViewItem, AncestorLevel=1}" Converter="{StaticResource ParentCountConverterInstance}" />
      </MultiBinding>
    </Grid.Width>   
   .... (content of the template) ....
</Grid>
</HierarchicalDataTemplate>

Первая привязка в мультисвязке получает ширину ScrollContentPresenter in TreeView, которая является общей видимой шириной TreeView.Вторая привязка вызывает конвертер с TreeViewItem в качестве аргумента и вычисляет, сколько родителей имеет TreeViewItem до достижения корневого элемента.Используя эти два входа, мы используем SumConverterInstance в Multibinding, чтобы вычислить доступную ширину для заданного TreeViewItem.

Вот экземпляры преобразователя, определенные в XAML:

  <my:SumConverter x:Key="SumConverterInstance" />
  <my:ParentCountConverter x:Key="ParentCountConverterInstance" />

икод для двух преобразователей:

// combine the width of the TreeView control and the number of parent items to compute available width
public class SumConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double totalWidth = (double)values[0];
        double parentCount = (double)values[1];
        return totalWidth - parentCount * 20.0;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

// count the number of TreeViewItems before reaching ScrollContentPresenter
public class ParentCountConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        int parentCount = 1;
        DependencyObject o = VisualTreeHelper.GetParent(value as DependencyObject);
        while (o != null && o.GetType().FullName != "System.Windows.Controls.ScrollContentPresenter")
        {
            if (o.GetType().FullName == "System.Windows.Controls.TreeViewItem")
                parentCount += 1;
            o = VisualTreeHelper.GetParent(o);
        }
        return parentCount;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Теперь это правильный вид:

альтернативный текст http://img249.imageshack.us/img249/6889/atts2.png

1 голос
/ 14 июля 2010

Вы должны позволить системе макета обработать это для вас, вместо того, чтобы принудительно устанавливать фиксированную ширину в ItemTemplate. Для ListBox все, что вам нужно сделать, это установить HorizontalContentAlignment="Stretch". Это первый шаг и для TreeView, но, к сожалению, в шаблоне по умолчанию есть другой макет, который требует дополнительных изменений. Он использует сетку из трех столбцов, которая помещает содержимое во второй столбец, а в третий (*) столбец расширяются только дочерние элементы. При добавлении Grid.ColumnSpan="2" к ContentPresenter, содержащему Border, содержимое будет растягиваться по всей области элемента.

<PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
<Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
    <Setter Property="Focusable" Value="False"/>
    <Setter Property="Width" Value="16"/>
    <Setter Property="Height" Value="16"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Border Background="Transparent" Height="16" Padding="5,5,5,5" Width="16">
                    <Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="Transparent" Stroke="#FF989898">
                        <Path.RenderTransform>
                            <RotateTransform Angle="135" CenterY="3" CenterX="3"/>
                        </Path.RenderTransform>
                    </Path>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF1BBBFA"/>
                        <Setter Property="Fill" TargetName="ExpandPath" Value="Transparent"/>
                    </Trigger>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="RenderTransform" TargetName="ExpandPath">
                            <Setter.Value>
                                <RotateTransform Angle="180" CenterY="3" CenterX="3"/>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="Fill" TargetName="ExpandPath" Value="#FF595959"/>
                        <Setter Property="Stroke" TargetName="ExpandPath" Value="#FF262626"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<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>
        <ToggleButton x:Name="Expander" ClickMode="Press" IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ExpandCollapseToggleStyle}"/>
        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"
                Grid.Column="1" Grid.ColumnSpan="2" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
            <ContentPresenter x:Name="PART_Header" ContentSource="Header" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
        </Border>
        <ItemsPresenter x:Name="ItemsHost" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1"/>
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger Property="IsExpanded" Value="false">
            <Setter Property="Visibility" TargetName="ItemsHost" Value="Collapsed"/>
        </Trigger>
        <Trigger Property="HasItems" Value="false">
            <Setter Property="Visibility" TargetName="Expander" Value="Hidden"/>
        </Trigger>
        <Trigger Property="IsSelected" Value="true">
            <Setter Property="Background" TargetName="Bd" 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 Property="Background" TargetName="Bd" 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>
0 голосов
/ 15 июля 2010

То, что вы описываете, это TreeView ItemContainerStyle, а сетка, которую я описываю выше, это HierarchicalDataTemplate.

В моем TreeView я показываю вложения электронной почты. TreeViewItem s содержат значок (первый столбец сетки), имя файла и размер (второй столбец) и время (третий столбец). Я хотел бы видеть элементы, растянутые или сжатые (оборачивая средний столбец сетки) на основе ширины TreeView.

Ниже приведен нерабочий пример. Как видите, имя файла не переносится, поэтому вы не можете видеть дату некоторых элементов.

альтернативный текст http://img638.imageshack.us/img638/1743/attsv.png

...