Search code examples
wpftreeviewwidthscrollviewertreeviewitem

Why my TreeViewItem text do not wrap in my sample code? How to make it wrap?


Why does my TreeViewItem does not wrap in my sample code?

enter image description here

Xaml:

<Window x:Class="WpfAppTestScrollViewBehavior.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfAppTestScrollViewBehavior"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:MainWindowModel></local:MainWindowModel>
    </Window.DataContext>

    <TabControl>
        <TabItem Header="Test">
            <TreeView ItemsSource="{Binding Level1s, Mode=OneWay}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                      Background="LightGoldenrodYellow">
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:Level1}"
                                            ItemsSource="{Binding Path=InstalledFontCollection}">
                        <Grid HorizontalAlignment="Stretch" Background="LightGreen">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"></ColumnDefinition>
                                <ColumnDefinition></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <CheckBox Grid.Column="0" IsChecked="{Binding Path=IsSelected}"></CheckBox>
                            <TextBlock Grid.Column="1" Text="{Binding Path=Name}" 
                                        TextWrapping="Wrap" Margin="5,0,0,0"></TextBlock>
                        </Grid>
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>
        </TabItem>
    </TabControl>
</Window>

Code:

using System;
using System.Collections.Generic;

namespace WpfAppTestScrollViewBehavior
{
    public class MainWindowModel
    {
        public List<Level1> Level1s { get; } = new List<Level1>();

        public MainWindowModel()
        {
            Level1s.Add(new Level1());
        }
    }
}


using System;
using System.Drawing.Text;

namespace WpfAppTestScrollViewBehavior
{
    public class Level1
    {
        public bool IsSelected { get; set; }
        public string Name { get; set; } = "a very long name in order to test text wrapping functionnality";

        public InstalledFontCollection InstalledFontCollection { get; } = new InstalledFontCollection();
    }
}

Just to prevent quick wrong answers:

You can add this code and it works fine:

<TabItem Header="Test 2">
    <ScrollViewer HorizontalScrollBarVisibility="Disabled">
        <Grid HorizontalAlignment="Stretch" Background="LightPink">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <CheckBox Grid.Column="0"></CheckBox>
            <TextBlock Grid.Column="1" Text="long name just for testing" 
                        TextWrapping="Wrap" Margin="5,0,0,0"></TextBlock>
        </Grid>
    </ScrollViewer>
</TabItem>

Results of previous working code (just as example of what i expect):

enter image description here


Solution

  • I came up with a solution that is generic and seems to works for any cases whatever content is inside TreeViewItem. It could be applied as a behavior (I favor this in order to make it simpler to use).

    One restriction of using this behavior is: Define the behavior as the last element in the TreeView xaml. By doing so it will support any additional ItemContainerStyle previously defined.

    Behavior code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Design;
    using System.Diagnostics;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Interactivity;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;
    using HQ.Wpf.Util;
    using HQ.Wpf.Util.Converter;
    
    namespace WpfAppTestScrollViewBehavior
    {
        public class BehaviorTreeView : Behavior<TreeView>
        {
            public static readonly DependencyProperty ShowHorizontalScrollBarProperty = DependencyProperty.Register(
                "PropertyType", typeof(bool), typeof(BehaviorTreeView), new PropertyMetadata(true));
    
            /// <summary>
            /// Settting this poroperty to false will fake a TreeView-ScrollView ViewPort finite width. 
            /// TreeViewItem will be advise to Calculate their width according to the width of the 
            /// ViewPort of the ScrollView of the TreeView.
            /// </summary>
            public bool ShowHorizontalScrollBar
            {
                get { return (bool)GetValue(ShowHorizontalScrollBarProperty); }
                set { SetValue(ShowHorizontalScrollBarProperty, value); }
            }
    
            // ******************************************************************   
            protected override void OnAttached()
            {
                base.OnAttached();
    
                if (!ShowHorizontalScrollBar)
                {
                    Style style = AssociatedObject.ItemContainerStyle ?? new Style(typeof(TreeViewItem));
                    var eventSetter = new EventSetter(FrameworkElement.LoadedEvent, new RoutedEventHandler(this.TreeViewItemLoaded));
                    style.Setters.Add(eventSetter);
                    AssociatedObject.ItemContainerStyle = style;
                }
            }
    
            // ******************************************************************   
            private void TreeViewItemLoaded(object sender, RoutedEventArgs e)
            {
                var tvi = sender as TreeViewItem;
                var contentPresenter = tvi.FindFirstChildWithNameRecursive<ContentPresenter>("PART_Header");
    
                var treeView = tvi.GetVisualParentRecursive<TreeView>();
    
                double offsetX = contentPresenter.TransformToAncestor(treeView).Transform(new Point(0, 0)).X;
    
                var scrollViewer = treeView.FindVisualChild<ScrollViewer>();
    
                Binding binding = new Binding();
                binding.Source = scrollViewer;
                binding.Path = new PropertyPath(nameof(ScrollViewer.ViewportWidth));
                binding.Mode = BindingMode.OneWay;
    
                var converter = new NumericFixedNumberAddedConverter();
                binding.Converter = converter;
                binding.ConverterParameter = -offsetX;
    
                BindingOperations.SetBinding(contentPresenter, FrameworkElement.WidthProperty, binding);
            }
    
            // ******************************************************************   
    
        }
    }
    

    Behavior Dependencies:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Data;
    
    namespace WpfAppTestScrollViewBehavior
    {
        class NumericFixedNumberAddedConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                try
                {
                    return (double) value + double.Parse(parameter.ToString());
                }
                catch (Exception ex)
                {
                    Debug.Print(ex.ToString());
                    return value;
                }
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Reflection;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Data;
    using System.Windows.Media;
    using System.Windows.Media.Media3D;
    
    namespace WpfAppTestScrollViewBehavior
    {
        public static class UiUtility
        {
            // ******************************************************************
            public static Window GetTopLevelOwnerWindowOrMainAppWindow(Control usercontrol)
            {
                return GetTopLevelOwnerWindow(usercontrol) ?? Application.Current.MainWindow;
            }
    
            // ******************************************************************
            public static Window GetTopLevelOwnerWindowOrMainAppWindow(DependencyObject dependencyObject)
            {
                return GetTopLevelOwnerWindow(dependencyObject) ?? Application.Current.MainWindow;
            }
    
            // ******************************************************************
            public static Window GetTopLevelOwnerWindow(Control usercontrol)
            {
                return GetTopLevelOwnerWindow((DependencyObject)usercontrol);
            }
    
            // ******************************************************************
            public static Window GetTopLevelOwnerWindow(DependencyObject dependencyObject)
            {
                while (dependencyObject != null && !(dependencyObject is Window))
                {
                    var dependencyObjectCopy = dependencyObject;
                    dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
    
                    if (dependencyObject == null)
                    {
                        dependencyObject = dependencyObjectCopy;
                        String propName = "DockSite";
                        PropertyInfo pi = dependencyObject.GetType().GetProperty(propName);
                        if (pi != null)
                        {
                            DependencyObject dependencyObjectTemp = null;
                            try
                            {
                                dependencyObjectTemp = dependencyObject.GetType().InvokeMember(propName,
                                    BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public, null, dependencyObject, null) as DependencyObject;
                            }
                            catch (Exception)
                            {
    
                            }
    
                            if (dependencyObjectTemp != null)
                            {
                                dependencyObject = LogicalTreeHelper.GetParent(dependencyObjectTemp);
                            }
                            else
                            {
                                dependencyObject = LogicalTreeHelper.GetParent(dependencyObject);
                            }
                        }
                        else
                        {
                            dependencyObject = LogicalTreeHelper.GetParent(dependencyObject);
                        }
                    }
                }
    
                return dependencyObject as Window;
            }
    
            // ******************************************************************
            public static T FindVisualParent<T>(DependencyObject element) where T : DependencyObject
            {
                var parent = element;
                while (parent != null)
                {
                    var correctlyTyped = parent as T;
                    if (correctlyTyped != null)
                    {
                        return correctlyTyped;
                    }
    
                    parent = VisualTreeHelper.GetParent(parent) as DependencyObject;
    
                }
                return null;
            }
    
            // ******************************************************************
            public static bool IsParentOf(DependencyObject parent, DependencyObject child)
            {
                if (parent == null || child == null)
                {
                    return false;
                }
    
                DependencyObject childParent = child;
                do
                {
                    if (childParent == parent)
                    {
                        return true;
                    }
    
                    childParent = VisualTreeHelper.GetParent(childParent);
                } while (childParent != null);
    
                return false;
            }
    
            // ******************************************************************
            public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
            {
                if (depObj != null)
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
                        if (child != null && child is T)
                        {
                            yield return (T)child;
                        }
    
                        foreach (T childOfChild in FindVisualChildren<T>(child))
                        {
                            yield return childOfChild;
                        }
                    }
                }
            }
    
            // ******************************************************************
            public static DataGridColumnHeader GetColumnHeaderFromColumn(DataGrid dataGrid, DataGridColumn column)
            {
                // dataGrid is the name of your DataGrid. In this case Name="dataGrid"
                foreach (var columnHeader in FindVisualChildren<DataGridColumnHeader>(dataGrid))
                {
                    if (columnHeader.Column == column)
                    {
                        return columnHeader;
                    }
                }
                return null;
            }
    
            // ******************************************************************
            public static void SafeInvoke(Action action)
            {
                if (Application.Current.Dispatcher.CheckAccess())
                {
                    action();
                }
                else
                {
                    Application.Current.Dispatcher.Invoke(action);
                }
            }
    
            // ******************************************************************
            public static void SafeBeginInvoke(Action action)
            {
                if (Application.Current.Dispatcher.CheckAccess())
                {
                    action();
                }
                else
                {
                    Application.Current.Dispatcher.BeginInvoke(action);
                }
            }
    
            // ******************************************************************
            public static void BindingRefresh(DependencyObject dependencyObject, DependencyProperty dependencyProperty)
            {
                BindingExpressionBase b = BindingOperations.GetBindingExpressionBase(dependencyObject, dependencyProperty);
                if (b != null)
                {
                    b.UpdateTarget();
                }
            }
    
            // ******************************************************************
            /// <summary>
            /// Finds a Child of a given item in the visual tree. 
            /// </summary>
            /// <param name="parent">A direct parent of the queried item.</param>
            /// <typeparam name="T">The type of the queried item.</typeparam>
            /// <param name="childName">x:Name or Name of child. </param>
            /// <returns>The first parent item that matches the submitted type parameter. 
            /// If not matching item can be found, 
            /// a null parent is being returned.</returns>
            public static T FindChild<T>(DependencyObject depObj, string childName)
                where T : DependencyObject
            {
                // Confirm obj is valid. 
                if (depObj == null) return null;
    
                // success case
                if (depObj is T && ((FrameworkElement)depObj).Name == childName)
                    return depObj as T;
    
                for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
    
                    //DFS
                    T obj = FindChild<T>(child, childName);
    
                    if (obj != null)
                        return obj;
                }
    
                return null;
            }
    
            // ******************************************************************
            public static void DebugPrintControlParentHierarchy(object frameworkElement)
            {
                StringBuilder hierarchy = new StringBuilder();
                var fe = frameworkElement as FrameworkElement;
                while (fe != null)
                {
                    hierarchy.Append(String.Format("{0} [{1}] ==> ", fe.GetType(), fe.Name));
                    fe = VisualTreeHelper.GetParent(fe) as FrameworkElement;
                }
    
                hierarchy.Append("!TOP!");
    
                Debug.Print(hierarchy.ToString());
            }
    
            // ******************************************************************
            /// <summary>
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="obj"></param>
            /// <returns></returns>
            public static T FindVisualChild<T>(this DependencyObject obj) where T : DependencyObject
            {
                if (obj != null && obj is Visual)
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                        var visualChild = child as T;
                        if (visualChild != null)
                            return visualChild;
                        else
                        {
                            T childOfChild = FindVisualChild<T>(child);
                            if (childOfChild != null)
                                return childOfChild;
                        }
                    }
                }
                return null;
            }
    
            // ******************************************************************
            public static FrameworkElement GetVisualParent(this UIElement element)
            {
                DependencyObject parent = VisualTreeHelper.GetParent(element);
                do
                {
                    var fe = parent as FrameworkElement;
                    if (fe != null)
                    {
                        return fe;
                    }
                    parent = VisualTreeHelper.GetParent(parent);
                } while (parent != null);
    
                return null;
            }
    
            // ******************************************************************
            public static void BringToFront(this Panel panel, UIElement element)
            {
                int maxIndex = 0;
                foreach (UIElement e in panel.Children)
                {
                    maxIndex = Math.Max(maxIndex, Panel.GetZIndex(e));
                }
    
                Panel.SetZIndex(element, maxIndex + 1);
            }
    
            //// ******************************************************************
            ///// <summary>
            ///// Return the center point of an direct child of a Canvas (not yet tested)
            ///// </summary>
            ///// <param name=""></param>
            ///// <param name="elementRelativeTo">If elementRelativeTo == null, will use direct parent</param>
            ///// <returns></returns>
            //public static Point GetCanvasElementCenterPoint(this FrameworkElement element)
            //{
            //  return new Point(
            //      Canvas.GetLeft(element) + (element.ActualWidth / 2),
            //      Canvas.GetTop(element) + (element.ActualHeight / 2));
            //}
    
            // ******************************************************************
            public enum PointPositionVertical
            {
                Top,
                Center,
                Bottom
            }
    
            // ******************************************************************
            public enum PointPositionHorizontal
            {
                Left,
                Center,
                Right
            }
    
            // ******************************************************************
            public static Point GetChildCoordinate(this UIElement elementContainer, FrameworkElement childElement,
                PointPositionHorizontal pointPositionHorizontal = PointPositionHorizontal.Left,
                PointPositionVertical pointPositionVertical = PointPositionVertical.Top)
            {
                double x;
                switch (pointPositionHorizontal)
                {
                    case PointPositionHorizontal.Center:
                        x = childElement.ActualWidth / 2;
                        break;
                    case PointPositionHorizontal.Right:
                        x = childElement.ActualWidth;
                        break;
                    default:
                        x = 0;
                        break;
                }
    
                double y;
                switch (pointPositionVertical)
                {
                    case PointPositionVertical.Center:
                        y = childElement.ActualHeight / 2;
                        break;
                    case PointPositionVertical.Bottom:
                        y = childElement.ActualHeight;
                        break;
                    default:
                        y = 0;
                        break;
                }
    
                return childElement.TranslatePoint(new Point(x, y), elementContainer);
            }
    
            // ******************************************************************
            public static void ApplyToEachVisualChildRecursively(this DependencyObject obj, Action<DependencyObject> action)
            {
                if (obj != null && obj is Visual)
                {
                    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
                    {
                        DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                        if (child != null)
                        {
                            action(child);
                            ApplyToEachVisualChildRecursively(child, action);
                        }
                    }
                }
            }
    
            // ******************************************************************
            public static T GetVisualParentRecursive<T>(this DependencyObject obj) where T : class
            {
                var element = obj as FrameworkElement;
                if (element != null)
                {
                    var frameWorkElement = VisualTreeHelper.GetParent(element) as FrameworkElement;
                    if (frameWorkElement != null)
                    {
                        var t = frameWorkElement as T;
                        if (t != null)
                        {
                            return t;
                        }
                        return frameWorkElement.GetVisualParentRecursive<T>();
                    }
                }
    
                return null;
            }
    
            // ******************************************************************
            public static T HitTest<T>(this Visual visual, Point pt) where T : class
            {
                T hitResult = null;
    
                VisualTreeHelper.HitTest(visual, null, result =>
                {
                    if (result.VisualHit is T)
                    {
                        hitResult = result.VisualHit as T;
                        return HitTestResultBehavior.Stop;
                    }
    
                    hitResult = result.VisualHit?.GetVisualParentRecursive<T>();
                    if (hitResult != null)
                    {
                        return HitTestResultBehavior.Stop;
                    }
    
                    return HitTestResultBehavior.Continue;
    
                }, new PointHitTestParameters(pt));
    
                return hitResult;
            }
    
            // ******************************************************************
            public static IEnumerable<T> GetChildrenRecursive<T>(this DependencyObject depObj) where T : class
            {
                int count = VisualTreeHelper.GetChildrenCount(depObj);
                for (int n = 0; n < count; n++)
                {
                    DependencyObject child = VisualTreeHelper.GetChild(depObj, n);
                    if (child is T)
                    {
                        yield return child as T;
                    }
    
                    foreach (T depObjChild in child.GetChildrenRecursive<T>())
                    {
                        yield return depObjChild;
                    }
                }
            }
    
            // ******************************************************************
            /// <summary>
            /// EO, 2017-05-11: New code
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="obj"></param>
            /// <param name="predicate"></param>
            /// <returns></returns>
            public static T GetVisualParentRecursive<T>(this DependencyObject obj, Predicate<T> predicate = null) where T : class
            {
                var element = obj as FrameworkElement;
                if (element != null)
                {
                    var frameWorkElement = VisualTreeHelper.GetParent(element) as FrameworkElement;
                    if (frameWorkElement != null)
                    {
                        var t = frameWorkElement as T;
                        if (t != null)
                        {
                            if (predicate == null || predicate(t))
                            {
                                return t;
                            }
                        }
                        return frameWorkElement.GetVisualParentRecursive<T>(predicate);
                    }
                }
    
                return null;
            }
    
            // ******************************************************************   
            /// <summary>
            /// EO, 2017-05-11: New code
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="parent"></param>
            /// <param name="name"></param>
            /// <returns></returns>
            public static T FindFirstChildWithNameRecursive<T>(this DependencyObject parent, string name) where T : FrameworkElement
            {
                return FindFirstChildRecursive(parent, (T child) => child.Name == name);
            }
    
            // ******************************************************************   
            /// <summary>
            /// Find all controls (visual or not)
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="parent"></param>
            /// <param name="predicate"></param>
            /// <returns></returns>
            public static T FindFirstChildRecursive<T>(this DependencyObject parent, Predicate<T> predicate = null) where T : DependencyObject
            {
                if (parent == null)
                {
                    return null;
                }
    
                //use the visual tree for Visual / Visual3D elements
                if (parent is Visual || parent is Visual3D)
                {
                    int count = VisualTreeHelper.GetChildrenCount(parent);
                    for (int i = 0; i < count; i++)
                    {
                        var child = VisualTreeHelper.GetChild(parent, i);
                        var childAsT = child as T;
                        if (childAsT != null)
                        {
                            if (predicate == null || predicate(childAsT))
                            {
                                return childAsT;
                            }
                        }
    
                        var result = FindFirstChildRecursive(child, predicate);
                        if (result != null)
                        {
                            return result;
                        }
                    }
                }
                else //use the logical tree for content / framework elements
                {
                    foreach (DependencyObject child in LogicalTreeHelper.GetChildren(parent))
                    {
                        var childAsT = child as T;
                        if (childAsT != null)
                        {
                            if (predicate == null || predicate(childAsT))
                            {
                                return childAsT;
                            }
                        }
    
                        var result = FindFirstChildRecursive(child, predicate);
                        if (result != null)
                        {
                            return result;
                        }
                    }
                }
    
                return null;
            }
    
            // ******************************************************************   
            /// <summary>
            /// Non recursive
            /// Based on stackoverflow: http://stackoverflow.com/questions/13248013/visualtreehelper-not-finding-children-of-dependencyobject-how-can-i-reliably-fi
            /// Find all controls (visual or not)
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="parent"></param>
            /// <param name="predicate"></param>
            /// <returns></returns>
            public static IEnumerable<T> FindChilds<T>(this DependencyObject parent, Predicate<T> predicate) where T : DependencyObject
            {
                if (parent == null) yield break;
    
                //use the visual tree for Visual / Visual3D elements
                if (parent is Visual || parent is Visual3D)
                {
                    int count = VisualTreeHelper.GetChildrenCount(parent);
                    for (int i = 0; i < count; i++)
                    {
                        var childAsT = VisualTreeHelper.GetChild(parent, i) as T;
                        if (childAsT != null)
                        {
                            if (predicate(childAsT))
                            {
                                yield return childAsT;
                            }
                        }
                    }
                }
                else //use the logical tree for content / framework elements
                {
                    foreach (DependencyObject obj in LogicalTreeHelper.GetChildren(parent))
                    {
                        var childAsT = obj as T;
                        if (childAsT != null)
                        {
                            if (predicate(childAsT))
                            {
                                yield return childAsT;
                            }
                        }
                    }
                }
            }
    
            // ******************************************************************   
            /// <summary>
            /// EO, 2017-05-11: New code
            /// Find all controls (visual or not)
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="parent"></param>
            /// <param name="name"></param>
            /// <returns></returns>
            public static List<T> FindChildsWithNameRecursive<T>(this DependencyObject parent, string name) where T : FrameworkElement
            {
                return FindChildsRecursive(parent, (T child) => child.Name == name);
            }
    
            // ******************************************************************   
            /// <summary>
            /// Find all controls (visual or not)
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="parent"></param>
            /// <param name="predicate"></param>
            /// <returns></returns>
            public static List<T> FindChildsRecursive<T>(this DependencyObject parent, Predicate<T> predicate = null) where T : DependencyObject
            {
                List<T> childs = new List<T>();
                return FindChildsRecursiveInternal(parent, predicate, childs);
            }
    
            // ******************************************************************   
            private static List<T> FindChildsRecursiveInternal<T>(this DependencyObject parent, Predicate<T> predicate, List<T> childs) where T : DependencyObject
            {
                if (parent != null)
                {
                    //use the visual tree for Visual / Visual3D elements
                    if (parent is Visual || parent is Visual3D)
                    {
                        int count = VisualTreeHelper.GetChildrenCount(parent);
                        for (int i = 0; i < count; i++)
                        {
                            var child = VisualTreeHelper.GetChild(parent, i);
                            var childAsT = child as T;
                            if (childAsT != null)
                            {
                                if (predicate == null || predicate(childAsT))
                                {
                                    childs.Add(childAsT);
                                }
                            }
    
                            FindChildsRecursiveInternal(child, predicate, childs);
                        }
                    }
                    else //use the logical tree for content / framework elements
                    {
                        foreach (DependencyObject child in LogicalTreeHelper.GetChildren(parent))
                        {
                            var childAsT = child as T;
                            if (childAsT != null)
                            {
                                if (predicate == null || predicate(childAsT))
                                {
                                    childs.Add(childAsT);
                                }
                            }
    
                            FindChildsRecursiveInternal(child, predicate, childs);
                        }
                    }
                }
    
                return childs;
            }
    
            // ******************************************************************   }
        }
    }
    

    Usage:

            <!-- ScrollViewer.HorizontalScrollBarVisibility="Disabled" -->
            <TreeView ItemsSource="{Binding Level1s, Mode=OneWay}">
    
                ...
    
                <i:Interaction.Behaviors>
                    <local:BehaviorTreeView ShowHorizontalScrollBar="False"></local:BehaviorTreeView>
                </i:Interaction.Behaviors>
    

    Thanks to Dean Kuga. Its solution give me the start to what I came up with.

    Results:

    enter image description here