zoukankan      html  css  js  c++  java
  • Wpf实现TreeSelect多选

    实现思路:

    1.定义一个树形控件对应的基类,有IsChecked属性。

    2.XAML里面绑定上面那个类对象。CheckBox绑定IsChecked属性。

    3.处理数据设置与文本显示,用逗号分开。

     XAML代码:同Wpf实现TreeSelect

    CS代码:和单选代码是在一块的,为了实现多选进行了改造。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace Util.Controls
    {
        /// <summary>
        /// TreeSelect.xaml 的交互逻辑
        /// </summary>
        [TemplatePart(Name = "PART_TreeView", Type = typeof(TreeView))]
        public partial class TreeSelect : ComboBox
        {
            public bool IsMulti
            {
                get { return (bool)GetValue(IsMultiProperty); }
                set { SetValue(IsMultiProperty, value); }
            }
    
            public static readonly DependencyProperty IsMultiProperty =
                DependencyProperty.Register("IsMulti", typeof(bool), typeof(TreeSelect), new PropertyMetadata(false));
    
            public IList SelectedItems
            {
                get { return (IList)GetValue(SelectedItemsProperty); }
                set { SetValue(SelectedItemsProperty, value); }
            }
    
            public static readonly DependencyProperty SelectedItemsProperty =
                DependencyProperty.Register("SelectedItems", typeof(IList), typeof(TreeSelect), new PropertyMetadata(OnSelectedItemsChanged));
    
            private static void OnSelectedItemsChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
                ((TreeSelect)sender).UpdateSelectedItems(e.NewValue as IList, e.OldValue as IList);
            }
    
            public new string DisplayMemberPath
            {
                get { return (string)GetValue(DisplayMemberPathProperty); }
                set { SetValue(DisplayMemberPathProperty, value); }
            }
    
            public new static readonly DependencyProperty DisplayMemberPathProperty =
                DependencyProperty.Register("DisplayMemberPath", typeof(string), typeof(TreeSelect));
    
            public new string SelectedValuePath
            {
                get { return (string)GetValue(SelectedValuePathProperty); }
                set { SetValue(SelectedValuePathProperty, value); }
            }
    
            public new static readonly DependencyProperty SelectedValuePathProperty =
                DependencyProperty.Register("SelectedValuePath", typeof(string), typeof(TreeSelect));
    
            /// <summary>
            /// Selected item of the TreeView
            /// </summary>
            public new object SelectedItem
            {
                get { return (object)GetValue(SelectedItemProperty); }
                set { SetValue(SelectedItemProperty, value); }
            }
    
            public new static readonly DependencyProperty SelectedItemProperty =
                DependencyProperty.Register("SelectedItem", typeof(object), typeof(TreeSelect), new PropertyMetadata(null, new PropertyChangedCallback(OnSelectedItemChanged)));
    
            private static void OnSelectedItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
                ((TreeSelect)sender).UpdateSelectedItem();
            }
    
            public new object SelectedValue
            {
                get { return (object)GetValue(SelectedValueProperty); }
                set { SetValue(SelectedValueProperty, value); }
            }
    
            public new static readonly DependencyProperty SelectedValueProperty =
                DependencyProperty.Register("SelectedValue", typeof(object), typeof(TreeSelect), new PropertyMetadata(null));
    
    
            public new string Text
            {
                get { return (string)GetValue(TextProperty); }
                set { SetValue(TextProperty, value); }
            }
    
            public static readonly DependencyProperty TextProperty =
                DependencyProperty.Register("Text", typeof(string), typeof(TreeSelect));
    
            /// <summary>
            /// Gets or sets text separator.
            /// </summary>
            public string TextSeparator
            {
                get { return (string)GetValue(TextSeparatorProperty); }
                set { SetValue(TextSeparatorProperty, value); }
            }
    
            public static readonly DependencyProperty TextSeparatorProperty =
                DependencyProperty.Register("TextSeparator", typeof(string), typeof(TreeSelect), new PropertyMetadata(","));
    
            /// <summary>
            /// Gets or sets max text length.
            /// </summary>
            public int? MaxTextLength
            {
                get { return (int?)GetValue(MaxTextLengthProperty); }
                set { SetValue(MaxTextLengthProperty, value); }
            }
    
            public static readonly DependencyProperty MaxTextLengthProperty =
                DependencyProperty.Register("MaxTextLength", typeof(int?), typeof(TreeSelect));
    
            /// <summary>
            /// Gets or sets text filler when text length exceeded.
            /// </summary>
            public string ExceededTextFiller
            {
                get { return (string)GetValue(ExceededTextFillerProperty); }
                set { SetValue(ExceededTextFillerProperty, value); }
            }
    
            public static readonly DependencyProperty ExceededTextFillerProperty =
                DependencyProperty.Register("ExceededTextFiller", typeof(string), typeof(TreeSelect), new PropertyMetadata("..."));
    
    
    
            static TreeSelect()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeSelect), new FrameworkPropertyMetadata(typeof(TreeSelect)));
            }
    
            public TreeSelect()
            {
                Loaded -= TreeSelect_Loaded;
                Loaded += TreeSelect_Loaded;
                SizeChanged -= TreeSelect_SizeChanged;
                SizeChanged += TreeSelect_SizeChanged;
            }
    
            private ExtendedTreeView _treeView;
    
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                this._treeView = Template.FindName("PART_TreeView", this) as ExtendedTreeView;
                if (this._treeView != null)
                {
                    //this._treeView.SelectedItemChanged += _TreeView_SelectedItemChanged;
                    _treeView.OnHierarchyMouseUp += OnTreeViewHierarchyMouseUp;
                    _treeView.AddHandler(System.Windows.Controls.TreeViewItem.SelectedEvent, new System.Windows.RoutedEventHandler(treeview_Selected));
                }
            }
    
            //protected override void OnDropDownClosed(EventArgs e)
            //{
            //    base.OnDropDownClosed(e);
            //    this.SelectedItem = _treeView.SelectedItem;
            //    this.UpdateText();
            //}
    
            //protected override void OnDropDownOpened(EventArgs e)
            //{
            //    base.OnDropDownOpened(e);
            //    this.UpdateText();
            //}
    
            private bool _interChanged = false;
            /// <summary>
            /// Handles clicks on any item in the tree view
            /// </summary>
            private void OnTreeViewHierarchyMouseUp(object sender, RoutedEventArgs e)
            {
                if (IsMulti)
                {
                    _interChanged = true;
                    SelectedItems.Clear();
                    foreach (var item in GenerateMultiObject(Items))
                    {
                        SelectedItems.Add(item);
                    }
                    _interChanged = false;
                }
                else
                {
                    //This line isn't obligatory because it is executed in the OnDropDownClosed method, but be it so
                    this.SelectedItem = _treeView.SelectedItem;                
                    base.SelectedItem = this.SelectedItem;
                    this.IsDropDownOpen = false;
                }
                this.UpdateText();
            }
    
            private void treeview_Selected(object sender, RoutedEventArgs e)
            {
                TreeViewItem item = (e.OriginalSource as TreeViewItem);
                if (item != null)
                {
                    item.BringIntoView();
                }
            }
    
            protected override bool IsItemItsOwnContainerOverride(object item)
            {
                if (item is ComboBoxItem)
                    return true;
                else
                    return false;
            }
    
            protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
            {
                var uie = element as FrameworkElement;
    
                if (!(item is ComboBoxItem))
                {
                    var textBinding = new Binding(DisplayMemberPath);
                    textBinding.Source = item;
                    uie.SetBinding(ContentPresenter.ContentProperty, textBinding);
                }
    
                base.PrepareContainerForItemOverride(element, item);
            }
    
            private void TreeSelect_SizeChanged(object sender, SizeChangedEventArgs e)
            {
                UpdateText();
            }
            private void TreeSelect_Loaded(object sender, RoutedEventArgs e)
            {
                UpdateText();
            }
    
            private void UpdateSelectedItem()
            {          
                if (this.SelectedItem == null || string.IsNullOrEmpty(this.SelectedValuePath))
                {
                    SelectedValue = null;
                }
                else
                {
                    SelectedValue = this.SelectedItem.GetType().GetProperty(SelectedValuePath).GetValue(this.SelectedItem, null);
                }
    
                base.SelectedItem = this.SelectedItem;
    
                UpdateText();
            }
    
            private void UpdateSelectedItems(IList newitem, IList olditem)
            {
                if (olditem != null)
                {
                    foreach (var item in olditem)
                    {
                        if (item.GetType().GetProperty("IsChecked") != null)
                        {
                            item.GetType().GetProperty("IsChecked").SetValue(item, false);
                        }
                    }
    
                    ((INotifyCollectionChanged)olditem).CollectionChanged -= TreeSelect_CollectionChanged;
                }
    
                if (newitem != null)
                {
                    foreach (var item in newitem)
                    {
                        if (item.GetType().GetProperty("IsChecked") != null)
                        {
                            item.GetType().GetProperty("IsChecked").SetValue(item, true);
                        }
                    }
    
                    ((INotifyCollectionChanged)newitem).CollectionChanged += TreeSelect_CollectionChanged;
                }
    
                UpdateText();
            }
    
            //主要是外部改变的时候更新数据,如果没有这种场景,可以不用。
            private void TreeSelect_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                if (_interChanged) return;
    
                if (e.NewItems != null)
                {
                    foreach (var item in e.NewItems)
                    {
                        if (item.GetType().GetProperty("IsCheckedOnlySelf") != null)
                        {
                            item.GetType().GetProperty("IsCheckedOnlySelf").SetValue(item, true);
                        }
                    }
                }
    
                if (e.OldItems != null)
                {
                    foreach (var item in e.OldItems)
                    {
                        if (item.GetType().GetProperty("IsCheckedOnlySelf") != null)
                        {
                            item.GetType().GetProperty("IsCheckedOnlySelf").SetValue(item, false);
                        }
                    }
                }
    
                UpdateText();
            }
    
            private void _TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
            {
                UpdateText();
                base.SelectedItem = this.SelectedItem;
            }
    
            private void UpdateText()
            {
                if (!IsLoaded)
                    return;
    
                if (IsMulti == false)
                {
                    Text = GenerateText(SelectedItem);
                }
                else
                {
                    Text = GenerateMultiText();
                }
            }
    
            public string GenerateText(object selectedItem)
            {
                var text = "";
                if (selectedItem == null)
                {
                    text = "";
                }
                else if (selectedItem is ComboBoxItem)
                {
                    var msi = selectedItem as ComboBoxItem;
                    text += msi.Content.ToString();
                }
                else
                {
                    if (!string.IsNullOrEmpty(DisplayMemberPath) && selectedItem.GetType().GetProperty(DisplayMemberPath) != null)
                        text += selectedItem.GetType().GetProperty(DisplayMemberPath).GetValue(selectedItem, null).ToString();
                    else
                        text += selectedItem.ToString();
    
                    if (selectedItem.GetType().GetProperty("IsSelected") != null)
                    {
                        selectedItem.GetType().GetProperty("IsSelected").SetValue(selectedItem, true);
                    }
                }
    
                return text;
            }
    
            private string GenerateMultiText()
            {
                var text = "";
                var isFirst = true;
              
                if (SelectedItems != null)
                {
                    foreach (var item in SelectedItems)
                    {
                        string txt = null;
                        if (item is ComboBoxItem)//这个还未支持多选,按单选处理
                        {
                            var msi = item as ComboBoxItem;
                            txt = msi.Content.ToString();
                        }
                        else
                        {
                            if (item.GetType().GetProperty("IsChecked") != null && item.GetType().GetProperty("IsChecked").GetValue(item, null).ToString() == "True")
                            {
                                if (!string.IsNullOrEmpty(DisplayMemberPath) && item.GetType().GetProperty(DisplayMemberPath) != null)
                                    txt = item.GetType().GetProperty(DisplayMemberPath).GetValue(item, null).ToString();
                                else
                                    txt = item.ToString();
                            }
                        }
    
                        if (!isFirst)
                            text += TextSeparator;
                        else
                            isFirst = false;
    
                        text += txt;
    
                        if (MaxTextLength == null)
                        {
                            if (!ValidateStringWidth(text + ExceededTextFiller))
                            {
                                if (text.Length == 0)
                                    return null;
                                text = text.Remove(text.Length - 1);
                                while (!ValidateStringWidth(text + ExceededTextFiller))
                                {
                                    if (text.Length == 0)
                                        return null;
                                    text = text.Remove(text.Length - 1);
                                }
                                return text + ExceededTextFiller;
                            }
                        }
                        else if (text.Length >= MaxTextLength)
                        {
                            return text.Cut((int)MaxTextLength, ExceededTextFiller);
                        }
                    }
                }
                return text;
            }
    
            private List<object> GenerateMultiObject(System.Collections.IEnumerable items)
            {
                List<object> objs = new List<object>();
                foreach (var item in items)
                {
                    object obj = null;
                    if (item is ComboBoxItem)//这个还未支持多选,按单选处理
                    {
                        var msi = item as ComboBoxItem;
                        if (msi.IsSelected)
                        {
                            obj = item;
                        }
                    }
                    else
                    {
                        if (item.GetType().GetProperty("IsChecked") != null && item.GetType().GetProperty("IsChecked").GetValue(item, null).ToString() == "True")
                        {
                            obj = item;
                        }
                    }
    
                    if (obj != null)
                        objs.Add(obj);
    
                    if (item.GetType().GetProperty("Children") != null)
                    {
                        objs.AddRange(GenerateMultiObject(item.GetType().GetProperty("Children").GetValue(item, null) as System.Collections.IEnumerable));
                    }
                }
    
                return objs;
            }
    
            private bool ValidateStringWidth(string text)
            {
                var size = MeasureString(text);
                if (size.Width > (ActualWidth - Padding.Left - Padding.Right - 30))
                    return false;
                else
                    return true;
    
            }
    
            private Size MeasureString(string candidate)
            {
                var formattedText = new FormattedText(candidate, System.Globalization.CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Brushes.Black, new NumberSubstitution(), TextFormattingMode.Display);
    
                return new Size(formattedText.Width, formattedText.Height);
            }
        }
    }
    

    然后绑定的类对象如下:

        public abstract class BaseTreeItemViewModel : INotifyPropertyChanged, IBaseTreeItemViewModel//组织机构树节点
        {
            #region 基本属性 
            private string _name;
            public string Name
            {
                get { return _name; }
                set
                {
                    _name = value;
                    OnPropertyChanged("Name");
                }
            }
    
            #endregion
    
            #region 
            private ObservableCollection<BaseTreeItemViewModel> _children;
            public ObservableCollection<BaseTreeItemViewModel> Children
            {
                get
                {
                    if (_children == null)
                    {
                        _children = new ObservableCollection<BaseTreeItemViewModel>();
                        _children.CollectionChanged += new NotifyCollectionChangedEventHandler(OnChildrenChanged);
                    }
    
                    return _children;
                }
            }
    
            protected void OnChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                // Note: This section does not account for multiple items being involved in change operations.
                // Note: This section does not account for the replace operation.
                if (e.Action == NotifyCollectionChangedAction.Add)
                {
                    BaseTreeItemViewModel child = (BaseTreeItemViewModel)e.NewItems[0];
                    child.Parent = this;
                }
                else if (e.Action == NotifyCollectionChangedAction.Remove)
                {
                    BaseTreeItemViewModel child = (BaseTreeItemViewModel)e.OldItems[0];
                    if (child.Parent == this)
                    {
                        child.Parent = null;
                    }
                }
            }
    
            public BaseTreeItemViewModel Parent { get; set; }
    
            private bool _isExpanded = true;
            public bool IsExpanded
            {
                get { return _isExpanded; }
                set
                {
                    if (_isExpanded != value)
                    {
                        _isExpanded = value;
                        OnPropertyChanged("IsExpanded");
                    }
                }
            }
    
            private bool _isSelected;
            public bool IsSelected
            {
                get { return _isSelected; }
                set
                {
                    if (_isSelected != value)
                    {
                        _isSelected = value;
                        OnPropertyChanged("IsSelected");
                    }
                }
            }
    
            private bool _isChecked = false;
            public bool IsChecked
            {
                get
                {
                    return _isChecked;
                }
                set
                {
                    if (_isChecked != value)
                    {
                        _isChecked = value;
                        OnPropertyChanged("IsChecked");
    
                        SetChildChecked(_isChecked);
                    }
                }
            }
    
            public bool IsCheckedOnlySelf
            {
                get
                {
                    return _isChecked;
                }
                set
                {
                    _isChecked = value;
                    OnPropertyChanged("IsChecked");
                }
            }
    
            private void SetChildChecked(bool isChecked)
            {
                if (Children != null)
                {
                    foreach (var child in Children)
                    {
                        child.IsChecked = isChecked;
                        child.SetChildChecked(isChecked);
                    }
                }
            }
    
            public void SetChecked(bool isChecked)
            {
                _isChecked = isChecked;
                OnPropertyChanged("IsChecked");
            }
    
            public int Level
            {
                get
                {
                    if (Parent == null)
                    {
                        return 0;
                    }
                    else
                    {
                        return Parent.Level + 1;
                    }
                }
            }
    
            #region TreeDataGrid专用
            public double MarginLeft
            {
                get
                {
                    return Level * 20 + 10;
                }
            }
            public Visibility ChildVisible
            {
                get
                {
                    if (Children.Count == 0)
                    {
                        return Visibility.Collapsed;
                    }
                    else
                    {
                        return Visibility.Visible;
                    }
                }
            }
    
            public void TreeDataGridOnPropertyChanged()
            {
                OnPropertyChanged("MarginLeft");
                OnPropertyChanged("ChildVisible");
            }
            #endregion
    
            /// <summary>
            /// 设置所有子项展开状态
            /// </summary>
            /// <param name="isExpanded"></param>
            public void SetChildrenExpanded(bool isExpanded)
            {
                foreach (BaseTreeItemViewModel child in Children)
                {
                    child.IsExpanded = isExpanded;
                    child.SetChildrenExpanded(isExpanded);
                }
            }
    
    
            public void InsertChild(int index, BaseTreeItemViewModel child)
            {
                if (!Children.Contains(child))
                {
                    child.Parent = this;
                    child.TreeDataGridOnPropertyChanged();
                    Children.Insert(index, child);
                    TreeDataGridOnPropertyChanged();
                }
            }
    
            public void AddChild(BaseTreeItemViewModel child)
            {
                if (!Children.Contains(child))
                {
                    child.Parent = this;
                    child.TreeDataGridOnPropertyChanged();
                    Children.Add(child);
                    TreeDataGridOnPropertyChanged();
                }
            }
    
            public void AddChildRange(IEnumerable<BaseTreeItemViewModel> childs)
            {
                foreach (var child in childs)
                {
                    if (!Children.Contains(child))
                    {
                        child.Parent = this;
                        child.TreeDataGridOnPropertyChanged();
                        Children.Add(child);
                    }
                }
                TreeDataGridOnPropertyChanged();
            }
    
            public void RemoveChild(BaseTreeItemViewModel child)
            {
                if (Children.Contains(child))
                {
                    child.Parent = null;
                    Children.Remove(child);
                    TreeDataGridOnPropertyChanged();
                }
            }
    
            public void ClearChild()
            {
                if (_children != null)
                {
                    _children.Clear();
                    TreeDataGridOnPropertyChanged();
                }
            }
            #endregion       
    
            public IEnumerable<BaseTreeItemViewModel> GetHierarchy()
            {
                return GetAscendingHierarchy().Reverse();
            }
    
            public IEnumerable<BaseTreeItemViewModel> GetChildren()
            {
                return this.Children;
            }
    
            private IEnumerable<BaseTreeItemViewModel> GetAscendingHierarchy()
            {
                var vm = this;
                yield return vm;
                while (vm.Parent != null)
                {
                    yield return vm.Parent;
                    vm = vm.Parent;
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }        
        }

    使用代码

    <util:TreeSelect
      Width="200"
      ItemsSource="{Binding Data}"
      SelectedItems="{Binding SelectedDatas}"
      ItemTemplate="{x:Null}"
      DisplayMemberPath="Name"
      IsMulti="True" xmlns:util="https://astudio.github.io/utilcontrol">
      <util:TreeSelect.Resources>
        <HierarchicalDataTemplate
          DataType="{x:Type viewmodel:Person}"
          ItemsSource="{Binding Path=Children}">
          <StackPanel
            Orientation="Horizontal">
            <CheckBox
              Margin="2,0,0,0"
              IsChecked="{Binding IsChecked,Mode=TwoWay}" />
            <Grid
              Margin="2,0,2,0">
              <TextBlock
                x:Name="txtName"
                Text="{Binding Name, Mode=TwoWay}"
                Width="Auto" />
            </Grid>
          </StackPanel>
        </HierarchicalDataTemplate>
      </util:TreeSelect.Resources>
    </util:TreeSelect>

    实现是实现了,绑定对象还需要有定义好的属性。

    这个东西在一些场景下没有必要封装成控件,其实就是一个树形控件的处理,封装在ComboBox里而已,不封装进行使用更加简单灵活。

    作者:竹天笑
    互相学习,提高自己。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
  • 相关阅读:
    Ch04-文字列表的设计
    ch03-文字版面的设计
    ch02-HTML的基本概念
    【转】网站建设常用工具
    XSY1036 [Apio2012]派遣
    HDU1512 Monkey King
    快速构造FFT/NTT
    支配树学习笔记
    BZOJ1026[SCOI2009]windy数
    扩展欧几里得算法(exGCD)学习笔记
  • 原文地址:https://www.cnblogs.com/akwkevin/p/14214764.html
Copyright © 2011-2022 走看看