如何在TreeView中获取TreeViewItem的可用(可见)宽度

时间:2010-07-13 17:20:27

标签: wpf treeview width treeviewitem

我有TreeViewItem s HierarchicalDataTemplate由一个包含3列的网格组成,其中包含以下定义:

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

我想设置网格的宽度,以便它会占用TreeViewItemTreeView的所有可用空间。因此,网格的第三列应在TreeView内右对齐。

如何获得网格宽度的正确值?

我知道对于ListBox ItemTemplate,我可以通过将宽度绑定到ScrollContentPresenter来设置宽度:

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

这个技巧在TreeView中不起作用,因为子项的可用空间比根树视图项少。

有什么想法吗?

3 个答案:

答案 0 :(得分:6)

在使用它几个小时后,我找到了一个使用多重绑定两个转换器的工作解决方案。

首先,XAML中HierarchicalDataTemplate的定义:

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

multibinding中的第一个绑定获得ScrollContentPresenterTreeView的宽度,这是TreeView的总可见宽度。第二个绑定调用带有TreeViewItem作为参数的转换器,并计算TreeViewItem在到达根项之前有多少父项。使用这两个输入,我们使用Multibinding中的SumConverterInstance来计算给定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();
    }
}

现在看起来是正确的:

alt text http://img249.imageshack.us/img249/6889/atts2.png

答案 1 :(得分:1)

您应该让布局系统为您处理此问题,而不是强制ItemTemplate中的固定宽度。对于ListBox,您只需设置HorizontalContentAlignment="Stretch"即可。这也是TreeView的第一步,但不幸的是,默认模板中还有一些需要进行额外更改的其他布局。它使用3列Grid,将内容放入第二列,只有子项扩展到第三列(*)。通过在包含Grid.ColumnSpan="2"的{​​{1}}上添加ContentPresenter,内容将覆盖整个项目的区域。

Border

答案 2 :(得分:0)

您所描述的是TreeView的{​​{1}},我上面描述的网格是ItemContainerStyle

在我的HierarchicalDataTemplate我正在显示电子邮件附件。 TreeView包含一个图标(网格的第一列),文件名和大小(第二列)和时间(第三列)。我希望根据TreeViewItem的宽度看到拉伸或挤压的项目(通过包裹网格的中间列)。

以下是一个非工作示例。正如您所看到的,文件名未被包装,因此您无法看到某些项目的日期。

alt text http://img638.imageshack.us/img638/1743/attsv.png