WPF treeview item background over entire row
Asked Answered
M

3

7

i am writing one app and i need to set background under entire row by items. I found some inspiration in here but i cannot get idea how to get Border background value in template (the problem is a bit more complicated:)

My problem is that in the treeview are 2 types of "data" (files and folders). User can modify the background under files and folders.

Right now i have the background on textblock, but it seems horrible and i want to have the background over entire row (i think it will looks much better).

Now it seems so:
img1

but what i need is:
img2

If i change the value of the border background, i change all items (logically). So i guess i really need to work with the textblock background, but i cannot reach spreading over entire row (stretch is not a solution, because it spread it just to the end of the line but not that white space before).

Thanks for advice.

EDIT : XAML in here:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:treeView">
<!-- TREEVIEW  -->
    <Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
        <Setter Property="Focusable" Value="False"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ToggleButton">
                    <Grid
          Width="15"
          Height="13"
          Background="Transparent">
                        <!--<Path x:Name="ExpandPath"
            HorizontalAlignment="Left" 
            VerticalAlignment="Center" 
            Margin="1,1,1,1"
            Fill="Red"
            Data="M 4 0 L 8 4 L 4 8 Z"/>-->
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked"
               Value="True">
                            <!--<Setter Property="Data"
                TargetName="ExpandPath"
                Value="M 0 4 L 8 4 L 4 8 Z"/>-->
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <Style x:Key="TreeViewItemFocusVisual">
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate>
                    <Border>
                        <Rectangle Margin="0,0,0,0"
                 StrokeThickness="5"
                 Stroke="Black"
                 StrokeDashArray="1 2"
                 Opacity="0"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>


    <Style x:Key="{x:Type TreeViewItem}" TargetType="{x:Type TreeViewItem}">
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="HorizontalContentAlignment" Value="{Binding Path=HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        <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}">
                    <ControlTemplate.Resources>
                        <local:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
                    </ControlTemplate.Resources>
                    <StackPanel>
                        <Border x:Name="Bd"
                          Background="{TemplateBinding Background}"
                          BorderBrush="{TemplateBinding BorderBrush}"
                          BorderThickness="{TemplateBinding BorderThickness}"
                          Padding="{TemplateBinding Padding}">
                            <Grid Margin="{Binding Converter={StaticResource lengthConverter}, RelativeSource={RelativeSource TemplatedParent}}">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="19" />
                                    <ColumnDefinition/>
                                </Grid.ColumnDefinitions>
                                <ToggleButton Grid.Column="1" x:Name="Expander"
                                  Style="{StaticResource ExpandCollapseToggleStyle}"
                                  IsChecked="{Binding Path=IsExpanded,
                                              RelativeSource={RelativeSource TemplatedParent}}"
                                  ClickMode="Press"/>
                                <ContentPresenter x:Name="PART_Header" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ContentSource="Header" />
                            </Grid>
                        </Border>
                        <ItemsPresenter x:Name="ItemsHost" />
                    </StackPanel>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsExpanded" Value="false">
                            <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                        <Trigger Property="HasItems" Value="false">
                            <Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
                        </Trigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="HasHeader" Value="false"/>
                                <Condition Property="Width" Value="Auto"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="PART_Header" Property="MinWidth" Value="75"/>
                        </MultiTrigger>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="HasHeader" Value="false"/>
                                <Condition Property="Height" Value="Auto"/>
                            </MultiTrigger.Conditions>
                            <Setter TargetName="PART_Header" Property="MinHeight" Value="19"/>
                        </MultiTrigger>
                        <Trigger Property="IsSelected" Value="true">
                            <Setter TargetName="Bd" Property="Background" 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 TargetName="Bd" Property="Background" 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>
</ResourceDictionary>
Mindszenty answered 5/4, 2013 at 5:34 Comment(1)
Can you write your XAML code?Cellulosic
N
-4

Use snoop to understand the visual tree you are trying to modify. This way it will take 2-3 minutes to actually know what you need to modify. ( google )

My bet is that you need to use something like this:

<TreeView>
<TreeView.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type TreeViewItem}}" TargetType="TreeViewItem">
                    <Setter Property="Background" Value="Purple" />
</TreeView.ItemContainerStyle>
</TreeView>

you will also need to add triggers if you want background colo depending on items.

Nette answered 5/4, 2013 at 5:51 Comment(5)
this seems like a answer..let me check it more in details..i will mark it like a answer i guess soon..thx!Mindszenty
it shifted me a bit, but problem is, when i put the itemContainterStyle in the code, just first top items are "purple"..Mindszenty
@piggy I am not sure this is going to work or not. Can you bind Background value to your view model? I haven't tried this, so not sure that this will work or not.Cellulosic
@Faisal background i can bind. in the XAML it is first setter in TreeViewItem. But this bind all items and subitems. The itemContainer looks that it will work, but problem is that it set the background just on the top level of the items :D aaaa i hate templating..:/ ^^Mindszenty
ehm ok, i am really stupid..:/..with this template it works simply set the background of the treeviewitem..:/ (before 2hours i had another template, where was something "wrong" and the background change didnt worked at all..i rewroted the template of treeview and it works now..:/..)..ok, give me minus, my big bad ^^, but the SNOOP helped me a lot..thx thx, my app is at the end..Mindszenty
S
10

Thanks for your solution! Here's is a preview what I've done so far:

preview

<TreeView Grid.Row="1"  Name="treeView" Margin="5">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="KeyboardNavigation.AcceptsReturn" Value="True" />
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            <!-- Style for the selected item -->
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TreeViewItem}">
                        <ControlTemplate.Resources>
                            <Conv:LeftMarginMultiplierConverter Length="19" x:Key="lengthConverter" />
                        </ControlTemplate.Resources>
                        <StackPanel>
                            <Border 
                                x:Name="Bd"
                                Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}"
                                Padding="{TemplateBinding Padding}">
                                <Grid Margin="{Binding Converter={StaticResource lengthConverter}, RelativeSource={RelativeSource TemplatedParent}}">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="19" />
                                        <ColumnDefinition/>
                                    </Grid.ColumnDefinitions>
                                    <ToggleButton 
                                        Grid.Column="0" 
                                        x:Name="Expander"
                                        Style="{StaticResource ExpandCollapseToggleStyle}"
                                        IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
                                        ClickMode="Press"/>
                                    <ContentPresenter x:Name="PART_Header" Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" ContentSource="Header" />
                                </Grid>
                            </Border>
                            <ItemsPresenter x:Name="ItemsHost" />
                        </StackPanel>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsExpanded" Value="false">
                                <Setter TargetName="ItemsHost" Property="Visibility" Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="HasItems" Value="false">
                                <Setter TargetName="Expander" Property="Visibility" Value="Hidden"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="HasHeader" Value="false"/>
                                    <Condition Property="Width" Value="Auto"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="PART_Header" Property="MinWidth" Value="75"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="HasHeader" Value="false"/>
                                    <Condition Property="Height" Value="Auto"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="PART_Header" Property="MinHeight" Value="19"/>
                            </MultiTrigger>
                            <Trigger Property="IsSelected" Value="true">
                                <!--<Setter TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>-->
                                <Setter TargetName="Bd" Property="Background" Value="#FFF7D280"/>
                                <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 TargetName="Bd" Property="Background" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>-->
                                <Setter TargetName="Bd" Property="Background" Value="#FFDADADA"/>
                                <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.Triggers>
                <!-- Selected and has focus -->
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="BorderBrush" Value="{StaticResource HT_Background_Orange}"/>
                </Trigger>
                <!-- Mouse over -->
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#FFFDE8BA"/>
                    <!--<Setter Property="Background">
                        <Setter.Value>
                            <LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
                                <GradientStop Color="#FFFAFBFD" Offset="0"/>
                                <GradientStop Color="#fadfa3" Offset="1"/>
                            </LinearGradientBrush>
                        </Setter.Value>
                    </Setter>-->
                    <Setter Property="BorderBrush" Value="#f59246"/>
                </Trigger>
                <!-- Selected but does not have the focus -->
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsSelected" Value="True"/>
                        <Condition Property="IsSelectionActive" Value="False"/>
                    </MultiTrigger.Conditions>
                    <Setter Property="BorderBrush" Value="#f59246"/>
                </MultiTrigger>
            </Style.Triggers>
            <Style.Resources>
                <Style TargetType="Border">
                    <Setter Property="CornerRadius" Value="0"/>
                </Style>
            </Style.Resources>
        </Style>
    </TreeView.ItemContainerStyle>
    <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="{x:Type local:FileSystemObjectInfo}" ItemsSource="{Binding Path=Children}">
            <StackPanel Orientation="Horizontal">
                <Image
                    Source="{Binding Path=ImageSource, UpdateSourceTrigger=PropertyChanged}" 
                    Margin="0,1,8,1"
                    Height="24"
                    Width="24"/>
                <TextBlock
                    Text="{Binding Path=FileSystemInfo.Name}"
                    VerticalAlignment="Center"/>
            </StackPanel>
        </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>
    <TreeView.Resources>

        <!-- Brushes for the selected item -->
        <LinearGradientBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFFDF2DA" Offset="0"/>
            <GradientStop Color="#FFF7D280" Offset="1"/>
        </LinearGradientBrush>
        <LinearGradientBrush x:Key="{x:Static SystemColors.ControlBrushKey}" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="White" Offset="0"/>
            <GradientStop Color="#FFE2E2E2" Offset="1"/>
        </LinearGradientBrush>
        <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
        <SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
    </TreeView.Resources>
</TreeView>
public class LeftMarginMultiplierConverter : IValueConverter
{
    public double Length { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        TreeViewItem item = value as TreeViewItem;
        if (item == null)
            return new Thickness(0);

        return new Thickness(Length * item.GetDepth(), 0, 0, 0);
    }

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

public static class TreeViewItemExtensions
{
    public static int GetDepth(this TreeViewItem item)
    {
        TreeViewItem parent;
        while ((parent = GetParent(item)) != null)
        {
            return GetDepth(parent) + 1;
        }
        return 0;
    }

    private static TreeViewItem GetParent(TreeViewItem item)
    {
        DependencyObject parent = item != null ? VisualTreeHelper.GetParent(item) : null;
        while (parent != null && !(parent is TreeViewItem || parent is TreeView))
        {
            parent = VisualTreeHelper.GetParent(parent);
        }
        return parent as TreeViewItem;
    }
}
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
    <Setter Property="Focusable" Value="False"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">
                <Grid
                Width="15"
                Height="13"
                Background="Transparent">
                    <Path 
                    x:Name="ExpandPath"
                    HorizontalAlignment="Left" 
                    VerticalAlignment="Center" 
                    Margin="1,1,1,1"
                    Fill="{StaticResource GlyphBrush}"
                    Data="M 4 0 L 8 4 L 4 8 Z"/>
                </Grid>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Data" TargetName="ExpandPath" Value="M 0 4 L 8 4 L 4 8 Z"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

If you have any questions, let me know!

Regards

Swing answered 13/6, 2016 at 7:55 Comment(3)
Cool!!! I was finding this solution, but is incomplete, converter and toogle style code are not present, but thanks.Sconce
I have added the LeftMarginMultiplierConverter and ExpandCollapseToggleStyle in my post.Swing
Saved me a ton of time! Thanks!Redingote
S
0

Completing Dominic Jonas's solution, using GetChildOfType method from How to get children of a WPF container by type?:

public class LeftMarginMultiplierConverter : IValueConverter
    {
        public int Length { get; set; }
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Thickness thickness = new Thickness(0, 0, 0, 0);
            if (value != null)
            {
                TreeViewItem treeViewItem = (TreeViewItem)value;
                ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(treeViewItem);
                if (parent != null)
                {
                    if (parent.GetType() == typeof(TreeView))
                    {
                        thickness = new Thickness(System.Convert.ToDouble(Length), 5, 5, 5);
                    }
                    else if (parent.GetType() == typeof(TreeViewItem))
                    {
                        Grid grid = (Grid) Helper.GetChildOfType<Grid>(parent);
                        if(grid != null)
                            thickness = new Thickness(System.Convert.ToDouble(grid.Margin.Left + Length), 5, 5, 5);
                    }
                }
            }
            return thickness;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    public static class Helper
    {
        public static T GetChildOfType<T>(this DependencyObject depObj) where T : DependencyObject
        {
            if (depObj == null) return null;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                var child = VisualTreeHelper.GetChild(depObj, i);

                var result = (child as T) ?? GetChildOfType<T>(child);
                if (result != null) return result;
            }
            return null;
        }
    }

And Highlight mouse over in only single row: How can I make WPF Trigger for IsMouseOver on TreeViewItem NOT affect all parents of the moused-over control?

Sconce answered 13/3, 2020 at 22:30 Comment(0)
N
-4

Use snoop to understand the visual tree you are trying to modify. This way it will take 2-3 minutes to actually know what you need to modify. ( google )

My bet is that you need to use something like this:

<TreeView>
<TreeView.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type TreeViewItem}}" TargetType="TreeViewItem">
                    <Setter Property="Background" Value="Purple" />
</TreeView.ItemContainerStyle>
</TreeView>

you will also need to add triggers if you want background colo depending on items.

Nette answered 5/4, 2013 at 5:51 Comment(5)
this seems like a answer..let me check it more in details..i will mark it like a answer i guess soon..thx!Mindszenty
it shifted me a bit, but problem is, when i put the itemContainterStyle in the code, just first top items are "purple"..Mindszenty
@piggy I am not sure this is going to work or not. Can you bind Background value to your view model? I haven't tried this, so not sure that this will work or not.Cellulosic
@Faisal background i can bind. in the XAML it is first setter in TreeViewItem. But this bind all items and subitems. The itemContainer looks that it will work, but problem is that it set the background just on the top level of the items :D aaaa i hate templating..:/ ^^Mindszenty
ehm ok, i am really stupid..:/..with this template it works simply set the background of the treeviewitem..:/ (before 2hours i had another template, where was something "wrong" and the background change didnt worked at all..i rewroted the template of treeview and it works now..:/..)..ok, give me minus, my big bad ^^, but the SNOOP helped me a lot..thx thx, my app is at the end..Mindszenty

© 2022 - 2024 — McMap. All rights reserved.