• WPF TreeView多选问题


    TreeView本身不支持多选,可以用一个附加属性实现

    一、附加属性定义

       public class TreeViewExtensions : DependencyObject
        {
            /// <summary>
            /// Gets the value of the dependency property "EnableMultiSelect".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <returns></returns>
            public static bool GetEnableMultiSelect(DependencyObject obj)
            {
                return (bool)obj.GetValue(EnableMultiSelectProperty);
            }
    
            /// <summary>
            /// Sets the value of the dependency property "EnableMultiSelect".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <param name="value"></param>
            public static void SetEnableMultiSelect(DependencyObject obj, bool value)
            {
                obj.SetValue(EnableMultiSelectProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for EnableMultiSelect.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty EnableMultiSelectProperty =
                DependencyProperty.RegisterAttached("EnableMultiSelect", typeof(bool), typeof(TreeViewExtensions), new FrameworkPropertyMetadata(false)
                {
                    PropertyChangedCallback = EnableMultiSelectChanged,
                    BindsTwoWayByDefault = true
                });
    
            /// <summary>
            /// Gets the value of the dependency property "SelectedItems".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <returns></returns>
            public static IList GetSelectedItems(DependencyObject obj)
            {
                return (IList)obj.GetValue(SelectedItemsProperty);
            }
    
            /// <summary>
            /// Sets the value of the dependency property "SelectedItems".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <param name="value"></param>
            public static void SetSelectedItems(DependencyObject obj, IList value)
            {
                obj.SetValue(SelectedItemsProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for SelectedItems.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty SelectedItemsProperty =
                DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(TreeViewExtensions), new PropertyMetadata(null));
    
            /// <summary>
            /// Gets the value of the dependency property "AnchorItem".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <returns></returns>
            static TreeViewItem GetAnchorItem(DependencyObject obj)
            {
                return (TreeViewItem)obj.GetValue(AnchorItemProperty);
            }
    
            /// <summary>
            /// Sets the value of the dependency property "AnchorItem".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <param name="value"></param>
            static void SetAnchorItem(DependencyObject obj, TreeViewItem value)
            {
                obj.SetValue(AnchorItemProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for AnchorItem.  This enables animation, styling, binding, etc...
            static readonly DependencyProperty AnchorItemProperty =
                DependencyProperty.RegisterAttached("AnchorItem", typeof(TreeViewItem), typeof(TreeViewExtensions), new PropertyMetadata(null));
    
            /// <summary>
            /// Gets the value of the dependency property "IsSelected".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <returns></returns>
            public static bool GetIsSelected(DependencyObject obj)
            {
                return (bool)obj.GetValue(IsSelectedProperty);
            }
    
            /// <summary>
            /// Sets the value of the dependency property "IsSelected".
            /// </summary>
            /// <param name="obj">Dependency Object</param>
            /// <param name="value"></param>
            public static void SetIsSelected(DependencyObject obj, bool value)
            {
                if (value)
                {
                    GradientStopCollection gradientStopCollection = new GradientStopCollection();
                    gradientStopCollection.Add(new GradientStop()
                    {
                        Color = (Color)ColorConverter.ConvertFromString("#FFC7DFFC"),
                        Offset = 1
                    });
                    gradientStopCollection.Add(new GradientStop()
                    {
                        Color = (Color)ColorConverter.ConvertFromString("#FF3832B8"),
                        Offset = 1
                    });
                    LinearGradientBrush brush = new LinearGradientBrush(gradientStopCollection, new Point(0.5, 0), new Point(0.5, 1));
                    (obj as TreeViewItem).Background = brush;
                }
                else
                {
                    (obj as TreeViewItem).Background = Brushes.Transparent;
                }
                obj.SetValue(IsSelectedProperty, value);
            }
    
            // Using a DependencyProperty as the backing store for IsSelected.  This enables animation, styling, binding, etc...
            public static readonly DependencyProperty IsSelectedProperty =
                DependencyProperty.RegisterAttached("IsSelected", typeof(bool), typeof(TreeViewExtensions), new PropertyMetadata(false)
                {
                    PropertyChangedCallback = RealSelectedChanged
                });
    
            /// <summary>
            /// "EnableMultiSelect" changed event.
            /// </summary>
            /// <param name="s">Dependency Object</param>
            /// <param name="args">Event parameter</param>
            static void EnableMultiSelectChanged(DependencyObject s, DependencyPropertyChangedEventArgs args)
            {
                TreeView tree = (TreeView)s;
                var wasEnable = (bool)args.OldValue;
                var isEnabled = (bool)args.NewValue;
                if (wasEnable)
                {
                    tree.RemoveHandler(TreeViewItem.MouseDownEvent, new MouseButtonEventHandler(ItemClicked));
                    tree.RemoveHandler(TreeView.KeyDownEvent, new KeyEventHandler(KeyDown));
                }
                if (isEnabled)
                {
                    tree.AddHandler(TreeViewItem.MouseDownEvent, new MouseButtonEventHandler(ItemClicked), true);
                    tree.AddHandler(TreeView.KeyDownEvent, new KeyEventHandler(KeyDown));
                }
            }
    
            /// <summary>
            /// Gets TreeView which contains the TreeViewItem.
            /// </summary>
            /// <param name="item">item</param>
            /// <returns>TreeView</returns>
            static TreeView GetTree(TreeViewItem item)
            {
                Func<DependencyObject, DependencyObject> getParent = (o) => VisualTreeHelper.GetParent(o);
                FrameworkElement currentItem = item;
                while (!(getParent(currentItem) is TreeView))
                {
                    currentItem = (FrameworkElement)getParent(currentItem);
                }
                return (TreeView)getParent(currentItem);
            }
    
            /// <summary>
            /// TreeViewItem seleted changed event.
            /// </summary>
            /// <param name="sender">sender</param>
            /// <param name="args">event parameter</param>
            static void RealSelectedChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
            {
                TreeViewItem item = (TreeViewItem)sender;
                var selectedItems = GetSelectedItems(GetTree(item));
                if (selectedItems != null)
                {
                    var isSelected = GetIsSelected(item);
                    if (isSelected)
                    {
                        try
                        {
                            selectedItems.Add(item.Header);
                        }
                        catch (ArgumentException)
                        {
                        }
                    }
                    else
                    {
                        selectedItems.Remove(item.Header);
                    }
                }
            }
    
            /// <summary>
            /// Key down event.
            /// </summary>
            /// <param name="sender">sender</param>
            /// <param name="e">event parameter</param>
            static void KeyDown(object sender, KeyEventArgs e)
            {
                TreeView tree = (TreeView)sender;
                if (e.Key == Key.A && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
                {
                    foreach (var item in GetExpandedTreeViewItems(tree))
                    {
                        SetIsSelected(item, true);
                    }
                    e.Handled = true;
                }
            }
    
            /// <summary>
            /// Item clicked event.
            /// </summary>
            /// <param name="sender">sender</param>
            /// <param name="e">event parameter</param>
            static void ItemClicked(object sender, MouseButtonEventArgs e)
            {
                TreeViewItem item = FindTreeViewItem(e.OriginalSource);
                if (item == null)
                {
                    return;
                }
                TreeView tree = (TreeView)sender;
    
                var mouseButton = e.ChangedButton;
                if (mouseButton != MouseButton.Left)
                {
                    if ((mouseButton == MouseButton.Right) && ((Keyboard.Modifiers & (ModifierKeys.Shift | ModifierKeys.Control)) == ModifierKeys.None))
                    {
                        if (GetIsSelected(item))
                        {
                            UpdateAnchorAndActionItem(tree, item);
                            return;
                        }
                        MakeSingleSelection(tree, item);
                    }
                    return;
                }
                if ((Keyboard.Modifiers & (ModifierKeys.Shift | ModifierKeys.Control)) != (ModifierKeys.Shift | ModifierKeys.Control))
                {
                    if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
                    {
                        MakeToggleSelection(tree, item);
                        return;
                    }
                    if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
                    {
                        MakeAnchorSelection(tree, item, true);
                        return;
                    }
                    MakeSingleSelection(tree, item);
                    return;
                }
            }
    
            /// <summary>
            /// Find TreeViewItem which contains the object.
            /// </summary>
            /// <param name="obj">obj</param>
            /// <returns></returns>
            private static TreeViewItem FindTreeViewItem(object obj)
            {
                DependencyObject dpObj = obj as DependencyObject;
                if (dpObj == null)
                {
                    return null;
                }
                if (dpObj is TreeViewItem)
                {
                    return (TreeViewItem)dpObj;
                }
                return FindTreeViewItem(VisualTreeHelper.GetParent(dpObj));
            }
    
            /// <summary>
            /// Gets all expanded TreeViewItems.
            /// </summary>
            /// <param name="tree">TreeView</param>
            /// <returns></returns>
            private static IEnumerable<TreeViewItem> GetExpandedTreeViewItems(ItemsControl tree)
            {
                for (int i = 0; i < tree.Items.Count; i++)
                {
                    var item = (TreeViewItem)tree.ItemContainerGenerator.ContainerFromIndex(i);
                    if (item == null)
                    {
                        continue;
                    }
                    yield return item;
                    if (item.IsExpanded)
                    {
                        foreach (var subItem in GetExpandedTreeViewItems(item))
                        {
                            yield return subItem;
                        }
                    }
                }
            }
    
            /// <summary>
            /// Select by Shift key.
            /// </summary>
            /// <param name="tree"></param>
            /// <param name="actionItem"></param>
            /// <param name="clearCurrent"></param>
            private static void MakeAnchorSelection(TreeView tree, TreeViewItem actionItem, bool clearCurrent)
            {
                if (GetAnchorItem(tree) == null)
                {
                    var selectedItems = GetSelectedTreeViewItems(tree);
                    if (selectedItems.Count > 0)
                    {
                        SetAnchorItem(tree, selectedItems[selectedItems.Count - 1]);
                    }
                    else
                    {
                        SetAnchorItem(tree, GetExpandedTreeViewItems(tree).Skip(3).FirstOrDefault());
                    }
                    if (GetAnchorItem(tree) == null)
                    {
                        return;
                    }
                }
    
                var anchor = GetAnchorItem(tree);
    
                var items = GetExpandedTreeViewItems(tree);
                bool betweenBoundary = false;
                foreach (var item in items)
                {
                    bool isBoundary = item == anchor || item == actionItem;
                    if (isBoundary)
                    {
                        betweenBoundary = !betweenBoundary;
                    }
                    if (betweenBoundary || isBoundary)
                    {
                        SetIsSelected(item, true);
                    }
                    else
                    {
                        if (clearCurrent)
                        {
                            SetIsSelected(item, false);
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
    
            /// <summary>
            /// Gets all selected TreeViewItems.
            /// </summary>
            /// <param name="tree">TreeView</param>
            /// <returns></returns>
            private static List<TreeViewItem> GetSelectedTreeViewItems(TreeView tree)
            {
                return GetExpandedTreeViewItems(tree).Where(i => GetIsSelected(i)).ToList();
            }
    
            /// <summary>
            /// Select by left mouse button.
            /// </summary>
            /// <param name="tree"></param>
            /// <param name="item"></param>
            private static void MakeSingleSelection(TreeView tree, TreeViewItem item)
            {
                foreach (TreeViewItem selectedItem in GetExpandedTreeViewItems(tree))
                {
                    if (selectedItem == null)
                    {
                        continue;
                    }
                    if (selectedItem != item)
                    {
                        SetIsSelected(selectedItem, false);
                    }
                    else
                    {
                        SetIsSelected(selectedItem, true);
                    }
                }
                UpdateAnchorAndActionItem(tree, item);
            }
    
            /// <summary>
            /// Select by Ctrl key.
            /// </summary>
            /// <param name="tree">TreeView</param>
            /// <param name="item">TreeViewItem</param>
            private static void MakeToggleSelection(TreeView tree, TreeViewItem item)
            {
                SetIsSelected(item, !GetIsSelected(item));
                UpdateAnchorAndActionItem(tree, item);
            }
    
            /// <summary>
            /// Update the Anchor TreeViewItem.
            /// </summary>
            /// <param name="tree">TreeView</param>
            /// <param name="item">TreeViewItem</param>
            private static void UpdateAnchorAndActionItem(TreeView tree, TreeViewItem item)
            {
                SetAnchorItem(tree, item);
            }
        }
    }

    二、使用

    <TreeView x:Name="TreeView" ItemsSource="{Binding Children}" 
    my:TreeViewExtensions.EnableMultiSelect="true"
    my:TreeViewExtensions.SelectedItems="{Binding SelectedTreeNodes}"/>

    三,自定义背景

    在TreeView.ItemContainerStyle中添加触发器
    <Trigger Property="my:TreeViewExtensions.IsSelected" Value="true">
    
    <Setter Property="Background">
    <Setter.Value>
    <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0">
    <GradientStop Color="#FFC7DFFC" Offset="1"/>
    <GradientStop Color="#FF3832B8" Offset="1"/>
    </LinearGradientBrush>
    </Setter.Value>
    </Setter>
    </Trigger>

    注意:上述代码未实现抬起按键清空,有时间整理一版本,增加一个抬起清空功能

  • 相关阅读:
    DevEco Studio常用设置
    idea报错:Failed to execute goal org.codehaus.mojo:execmavenplugin:3.0.0:exec (defaultcli) on
    HarmonyOS实战 — 第一个入门 HelloWorld
    RabbitMQ入门之HelloWorld
    鸿蒙系统简介
    阿里云短信验证出现签名不合法(不存在或被拉黑)
    鸿蒙和安卓的对比
    关于 SVN 没有加锁 却在 update 和 commit 时 提示 有锁
    ibatis xml 数据库 批量新增 与批量删除
    看不懂的类型转换(有待解决)
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/15125759.html
Copyright © 2020-2023  润新知