views:

3684

answers:

1

I want to create a mix of a treeview and a listview. I want to have 2 columns. In the left column i want a recursive treeview and the right column should show some info about the items from the left column. Let's call the left column Name and the right column Value. The problem is that when you expand a treeview the indentation level changes and the Value-column becomes unaligned.

All i can come up with is to either:

A: Use the built in TreeView and manually change the width of the Name-column depending on the indentation level so the value-column always is aligned.

B. Use the built in ListView and manually create a TreeView by adding childitems between the parent items and also change the indentation of these.

Is there really no better way?

+2  A: 

There is a way, I have such a beast in a Silverlight application

You need to tweek the template of the treeviewitem. The default template does not extend all the way accross the treeview.

By tweeking the template, you can get it to extend all the way, and you can then set your DataTemplate (or HierarchicalDataTemplate) to a grid. If I remember correctly, you need to get a copy of the default template of the TreeviewItem and change the HorizontalAlignment property of the "Header" element to "Stretch", remove the rightmost column in the grid that makes up the template, and change the width of the column containing the element from "Auto" to "*".

It's fairly easy to do using blend. Create a TreeViewItem, right click on it and select "edit control parts ("Template") > Edit a copy..." This will create a copy of the default template for the TreeViewItem. From there, locate the ContentPresenter named PART_Header. Walking up from this, find the containing grid and modify its columns to match my explanantion (remove the last column, change the second column from "Auto" to "*"). In the Style that was created for the item, set the HorizontalContentAlignment to "Stretch" (it is bound to something else by default).

Use the Style that was created as the ItemContainerStyle on your treeview. You can delete the TreeViewItem you created at first after that.

At the end, you shuold end up with a bunch of resources, one of which is your style. See below for what I end up with (both the TreeViewItem style and a basic DataTemplate for my items with a name and value columns). There are other resources created that the TreeViewItem style/template reference, but they are not shown here (because it is already way too long :p).

<Window.Resources>    
<Style x:Key="TreeViewItemStyle1" TargetType="{x:Type TreeViewItem}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        <Setter Property="VerticalContentAlignment" Value="{Binding Path=VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <Setter Property="Padding" Value="1,0,0,0"/>
        <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
        <Setter Property="FocusVisualStyle" Value="{StaticResource TreeViewItemFocusVisual}"/>
        <Setter Property="Template">
         <Setter.Value>
          <ControlTemplate TargetType="{x:Type TreeViewItem}">
           <Grid>
            <Grid.ColumnDefinitions>
             <ColumnDefinition MinWidth="19" Width="Auto"/>
             <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
             <RowDefinition Height="Auto"/>
             <RowDefinition/>
            </Grid.RowDefinitions>
            <ToggleButton x:Name="Expander" Style="{StaticResource ExpandCollapseToggleStyle}" ClickMode="Press" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"/>
            <Border x:Name="Bd" SnapsToDevicePixels="true" Grid.Column="1" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}">
             <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" x:Name="PART_Header" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" ContentSource="Header"/>
            </Border>
            <ItemsPresenter x:Name="ItemsHost" Grid.Column="1" Grid.ColumnSpan="2" 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>
         </Setter.Value>
        </Setter>
    </Style>

        <HierarchicalDataTemplate x:Key="DataTemplate1"                                   
                                  ItemsSource="{Binding SubNodes}">
            <Grid Margin="5">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <TextBlock HorizontalAlignment="Left"
                           VerticalAlignment="Top"
                           Grid.Column="1"
                           Text="HEADER"
                           TextWrapping="Wrap" />
                <TextBox HorizontalAlignment="Left"
                         Margin="2"
                         VerticalAlignment="Top"
                         Text="VALUE"
                         TextWrapping="Wrap"
                         Grid.Column="2" />
            </Grid>

</Window.Resources>

<TreeView HorizontalAlignment="Stretch"
                  VerticalAlignment="Stretch"
                  Width="Auto"
                  Height="Auto"
                  x:Name="trv"
       ItemContainerStyle="{StaticResource TreeViewItemStyle1}"
                  ItemTemplate="{DynamicResource DataTemplate1}">            
        </TreeView>

Note that you need to ensure your grid columns containing the cells on the right will always be the same width, otherwise you'll have something weird (I use "Auto" columns with a fixed width content, and I add a blank "*" column between the name and the "cells" to right-align them).

Also note that this solution basically "freezes" the appearance of the treeview to whatever theme you have on your machine. (It will look the same on a Vista and XP machine, based on the OS you used when making the modification).

Denis Troller