Reputation: 35450
So the requirement is simple, but the solution doesn't seem to be (or at least I haven't succeeded yet). I need to display a vertical bar at the left side of the currently selected item of the TreeView
control. Something like this:
Problem I'm facing is that with child items, this indicator also moves towards right, as it is part of the ItemTemplate
, like this:
This is undesirable. I need the red indicator to stick to the left edge of the control, like this:
I can see why this happens. The ItemsPresenter
in TreeViewItem
template introduces a left margin of 16 units, which causes the all child items to move right-wards as well. I can't figure out how to avoid it.
Note: The red bar is a Border
with StrokeThickness
set to 4,0,0,0
. It encompasses the Image
and TextBlock
elements inside it, though this doesn't directly have anything to do with the problem.
Upvotes: -2
Views: 191
Reputation: 3601
As you are aware, since the left vacant space is outside of ItemsPresenter
which hosts the content of TreeViewItem
, you cannot accomplish it by ordinary Style.
Instead, a workaround would be to change the bar to an element such as Rentangle
and move it to the edge of TreeView
. For example, it can be done by an attached property which is to be attached to the element and move it to the edge of TreeView with a specified left margin.
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
public static class TreeViewHelper
{
public static double? GetLeftMargin(DependencyObject obj)
{
return (double?)obj.GetValue(LeftMarginProperty);
}
public static void SetLeftMargin(DependencyObject obj, double value)
{
obj.SetValue(LeftMarginProperty, value);
}
public static readonly DependencyProperty LeftMarginProperty =
DependencyProperty.RegisterAttached("LeftMargin", typeof(double?), typeof(TreeViewHelper), new PropertyMetadata(null, OnValueChanged));
private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((d is FrameworkElement element) && (e.NewValue is double leftMargin))
{
element.Loaded += (_, _) =>
{
TreeView? tv = GetTreeView(element);
if (tv is null)
return;
Point relativePosition = element.TransformToAncestor(tv).Transform(new Point(0, 0));
element.RenderTransform = new TranslateTransform(leftMargin - relativePosition.X, 0);
};
}
}
private static TreeView? GetTreeView(FrameworkElement element)
{
DependencyObject test = element;
while (test is not null)
{
test = VisualTreeHelper.GetParent(test);
if (test is TreeView tv)
return tv;
}
return null;
}
}
Edit:
This workaround does not depend on how to show/hide the bar upon selection of the ListViewItem
. Although the question does not provide the actual code for this, if you implement a mechanism to change BorderBrush
upon selelection, you can modify it to change Fill
of the bar (in the case of Rectangle
).
Upvotes: 0