TreeView:沿着控件左边缘的项目放置选择指示器

发布于 2025-01-18 00:44:19 字数 889 浏览 2 评论 0原文

所以需求很简单,但解决方案似乎并不简单(或者至少我还没有成功)。我需要在 TreeView 控件当前所选项目的左侧显示一个垂直条。像这样的东西:

在此处输入图像描述

我面临的问题是,对于子项目,该指示器也会向右移动,因为它是 ItemTemplate 的一部分,如下所示:

在此处输入图像描述

这是不可取的。我需要红色指示器粘在控件的左边缘,如下所示:

在此处输入图像描述

我明白为什么会发生这种情况。 TreeViewItem 模板中的 ItemsPresenter 引入了 16 个单位的左边距,这会导致所有子项也向右移动。我不知道如何避免它。

注意:红色条是 BorderStrokeThickness 设置为 4,0,0,0。它包含其中的 ImageTextBlock 元素,尽管这与问题没有直接关系。

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:

enter image description here

Problem I'm facing is that with child items, this indicator also moves towards right, as it is part of the ItemTemplate, like this:

enter image description here

This is undesirable. I need the red indicator to stick to the left edge of the control, like this:

enter image description here

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

许久 2025-01-25 00:44:19

如您所知,由于左空置空间不在itempresenter托管treeviewItem的内容之外,因此您无法通过普通样式完成它。

取而代之的是,解决方法是将栏更改为rentangle之类的元素,然后将其移至treeview的边缘。例如,它可以通过附加的属性来完成,该属性将附加到元素上,并使用指定的左边边缘将其移至树景的边缘。

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

编辑:

此解决方法不取决于选择listViewItem时如何显示/隐藏栏。尽管该问题不能为此提供实际代码,但是如果您在selelelecth时实现了更改borderbrush的机制,则可以将其修改为更改bar的fill(在矩形的情况。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文