珂珂的个人博客 - 一个程序猿的个人网站

WPF 自定义带Checkbox、图标 的TreeView

     首先声明,这个大部分参照了2位大神,我做了一小部分改动。原来的控件选择状态只能是false或true,我加入了第三种状态Null,就是部分选中状态。另外在Model 里面加了一个 Tag属性,将IList改为ObservableCollection<T> 还完善了ItemSource 为null点右键菜单报错的判断。

原文地址是http://www.cnblogs.com/zsmhhfy/archive/2013/03/18/2965755.html

http://blog.csdn.net/kunoy/article/details/11603933

主要改动是SetParentChecked  和SetChildrenChecked 这两个方法,把Model的IsChecked由bool 类型改为 bool?

 public void SetParentChecked()
        {
            if (Parent != null)
            {
                if (Parent.Children.Where(c => c.IsChecked == null).Count() > 0)
                {
                    Parent.IsChecked = null;
                }
                else if (Parent.Children.Where(c => c.IsChecked == true).Count() == 0)
                {
                    Parent.IsChecked = false;
                }
                else if (Parent.Children.Where(c => c.IsChecked == true).Count() == Parent.Children.Count)
                {
                    Parent.IsChecked = true;
                }
                else if (Parent.Children.Where(c => c.IsChecked == true).Count() < Parent.Children.Count)
                {
                    Parent.IsChecked = null;
                }

                if (Parent.Parent != null)
                {
                    Parent.SetParentChecked();
                }
            }
        }

所有改动后的代码如下

XAML

<UserControl x:Class="FYJ.Windows.Controls.CustomTreeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:FYJ.Windows.Controls" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <DockPanel>
         
            <Border>
                <TreeView Name="tvZsmTree">
                    <TreeView.ContextMenu>
                        <ContextMenu>
                            <MenuItem Name="menuExpandAll" Header="全部展开" Click="menuExpandAll_Click">
                                <MenuItem.Icon>
                                    <Image Source="Images/16_16/folder_open_arrow.png" />
                                </MenuItem.Icon>
                            </MenuItem>
                            <MenuItem Name="menuUnExpandAll" Header="全部折叠" Click="menuUnExpandAll_Click">
                                <MenuItem.Icon>
                                    <Image Source="Images/16_16/folder_close_arrow.png" />
                                </MenuItem.Icon>
                            </MenuItem>
                            <MenuItem Name="menuSelectAll" Header="全部选中" Click="menuSelectAll_Click">
                                <MenuItem.Icon>
                                    <Image Source="Images/16_16/tick.png" />
                                </MenuItem.Icon>
                            </MenuItem>
                            <MenuItem Name="menuUnSelectAll" Header="全部取消" Click="menuUnSelectAll_Click">
                                <MenuItem.Icon>
                                    <Image Source="Images/16_16/delete.png" />
                                </MenuItem.Icon>
                            </MenuItem>
                        </ContextMenu>
                    </TreeView.ContextMenu>
                    <TreeView.ItemContainerStyle>
                        <Style TargetType="TreeViewItem">
                            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"></Setter>
                            <EventSetter Event="TreeViewItem.PreviewMouseRightButtonDown" Handler="TreeViewItem_PreviewMouseRightButtonDown"/>
                        </Style>
                    </TreeView.ItemContainerStyle>
                    <TreeView.ItemTemplate>
                        <HierarchicalDataTemplate DataType="{x:Type local:TreeModel}"  ItemsSource="{Binding Children}">
                            <StackPanel  Margin="-2,0,0,0" Orientation="Horizontal" x:Name="staTree">
                                <CheckBox ToolTip="{Binding ToolTip}" FontSize="14" FontFamily="微软雅黑" Tag="{Binding Children}" IsChecked="{Binding IsChecked, Mode=TwoWay}" Click="treeNode_Click">
                                    <StackPanel Orientation="Horizontal">
                                        <Image VerticalAlignment="Center" Source="{Binding Icon}" ></Image>
                                        <TextBlock Text="{Binding Name}"></TextBlock>
                                    </StackPanel>
                                    <CheckBox.ContextMenu>
                                        <ContextMenu>
                                            <MenuItem Name="menuSelectAllChild" Header="全部选中子项" Click="menuSelectAllChild_Click">
                                                <MenuItem.Icon>
                                                    <Image Source="Images/16_16/tick.png" />
                                                </MenuItem.Icon>
                                            </MenuItem>
                                        </ContextMenu>
                                    </CheckBox.ContextMenu>
                                </CheckBox>
                            </StackPanel>
                            <HierarchicalDataTemplate.Triggers>
                                <DataTrigger Binding="{Binding IsChecked}" Value="true">
                                    <Setter TargetName="staTree" Property="Background" Value="White"/>
                                </DataTrigger>
                            </HierarchicalDataTemplate.Triggers>
                        </HierarchicalDataTemplate>
                    </TreeView.ItemTemplate>
                </TreeView>
            </Border>
        </DockPanel>
    </Grid>
</UserControl>

Model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace FYJ.Windows.Controls
{
    public class TreeModel : INotifyPropertyChanged
    {
        #region 私有变量
        /// <summary>
        /// Id值
        /// </summary>
        private string _id;
        /// <summary>
        /// 显示的名称
        /// </summary>
        private string _name;
        /// <summary>
        /// 图标路径
        /// </summary>
        private string _icon;
        /// <summary>
        /// 选中状态
        /// </summary>
        private bool? _isChecked;
        /// <summary>
        /// 折叠状态
        /// </summary>
        private bool _isExpanded;
        /// <summary>
        /// 子项
        /// </summary>
        private IList<TreeModel> _children;
        /// <summary>
        /// 父项
        /// </summary>
        private TreeModel _parent;
        #endregion

        /// <summary>
        /// 构造
        /// </summary>
        public TreeModel()
        {
            Children = new ObservableCollection<TreeModel>();
            _isChecked = false;
            IsExpanded = false;
            _icon = "/Images/16_16/folder_go.png";
        }

        /// <summary>
        /// 键值
        /// </summary>
        public string Id
        {
            get { return _id; }
            set { _id = value; }
        }

        /// <summary>
        /// 显示的字符
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        /// <summary>
        /// 图标
        /// </summary>
        public string Icon
        {
            get { return _icon; }
            set { _icon = value; }
        }

        /// <summary>
        /// 指针悬停时的显示说明
        /// </summary>
        public string ToolTip
        {
            get
            {
                return String.Format("{0}-{1}", Id, Name);
            }
        }

        /// <summary>
        /// 是否选中
        /// </summary>
        public bool? IsChecked
        {
            get
            {
                return _isChecked;
            }
            set
            {
                if (value != _isChecked)
                {
                    _isChecked = value;
                    NotifyPropertyChanged("IsChecked");
                }
            }
        }

        /// <summary>
        /// 是否展开
        /// </summary>
        public bool IsExpanded
        {
            get { return _isExpanded; }
            set
            {
                if (value != _isExpanded)
                {
                    //折叠状态改变
                    _isExpanded = value;
                    NotifyPropertyChanged("IsExpanded");
                }
            }
        }

        /// <summary>
        /// 父项
        /// </summary>
        public TreeModel Parent
        {
            get { return _parent; }
            set { _parent = value; }
        }

        /// <summary>
        /// 子项
        /// </summary>
        public IList<TreeModel> Children
        {
            get { return _children; }
            set { _children = value; }
        }

        /// <summary>
        /// 设置所有子项的选中状态
        /// </summary>
        /// <param name="isChecked"></param>
        public void SetChildrenChecked()
        {
            if (Children != null && Children.Count > 0 && IsChecked != null)
            {
                foreach (TreeModel child in Children)
                {
                    child.IsChecked = IsChecked;
                    if (child.Children != null && Children.Count > 0)
                    {
                        child.SetChildrenChecked();
                    }
                }
            }
        }

        public void SetChildrenChecked(bool isCheck)
        {
            if (Children != null && Children.Count > 0)
            {
                foreach (TreeModel child in Children)
                {
                    child.IsChecked = isCheck;
                    if (child.Children != null && Children.Count > 0)
                    {
                        child.SetChildrenChecked(isCheck);
                    }
                }
            }
        }

        /// <summary>
        /// 设置所有子项展开状态
        /// </summary>
        /// <param name="isExpanded"></param>
        public void SetChildrenExpanded(bool isExpanded)
        {
            foreach (TreeModel child in Children)
            {
                child.IsExpanded = isExpanded;
                child.SetChildrenExpanded(isExpanded);
            }
        }

        /// <summary>
        /// 属性改变事件
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #region 新加

        /// <summary>
        /// 附加属性
        /// </summary>
        /// fangyj
        public object Tag { get; set; }


        public void SetParentChecked()
        {
            if (Parent != null)
            {
                if (Parent.Children.Where(c => c.IsChecked == null).Count() > 0)
                {
                    Parent.IsChecked = null;
                }
                else if (Parent.Children.Where(c => c.IsChecked == true).Count() == 0)
                {
                    Parent.IsChecked = false;
                }
                else if (Parent.Children.Where(c => c.IsChecked == true).Count() == Parent.Children.Count)
                {
                    Parent.IsChecked = true;
                }
                else if (Parent.Children.Where(c => c.IsChecked == true).Count() < Parent.Children.Count)
                {
                    Parent.IsChecked = null;
                }

                if (Parent.Parent != null)
                {
                    Parent.SetParentChecked();
                }
            }
        }

        #endregion

    }
}


XAML后台

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace FYJ.Windows.Controls
{
    /// <summary>
    /// ZsmTreeView.xaml 的交互逻辑
    /// </summary>
    public partial class CustomTreeView : UserControl
    {
        #region 私有变量属性
        /// <summary>
        /// 控件数据
        /// </summary>
        private IList<TreeModel> _itemsSourceData;
        #endregion

        /// <summary>
        /// 构造
        /// </summary>
        public CustomTreeView()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 控件数据
        /// </summary>
        public IList<TreeModel> ItemsSourceData
        {
            get { return _itemsSourceData; }
            set
            {
                _itemsSourceData = value;
                tvZsmTree.ItemsSource = _itemsSourceData;
            }
        }

        /// <summary>
        /// 设置对应Id的项为选中状态
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int SetCheckedById(string id, IList<TreeModel> treeList)
        {
            foreach (var tree in treeList)
            {
                if (tree.Id.Equals(id))
                {
                    tree.IsChecked = true;
                    return 1;
                }
                if (SetCheckedById(id, tree.Children) == 1)
                {
                    return 1;
                }
            }

            return 0;
        }
        /// <summary>
        /// 设置对应Id的项为选中状态
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public int SetCheckedById(string id)
        {
            foreach (var tree in ItemsSourceData)
            {
                if (tree.Id.Equals(id))
                {
                    tree.IsChecked = true;
                    return 1;
                }
                if (SetCheckedById(id, tree.Children) == 1)
                {
                    return 1;
                }
            }

            return 0;
        }

        /// <summary>
        /// 获取选中项
        /// </summary>
        /// <returns></returns>
        public IList<TreeModel> CheckedItemsIgnoreRelation()
        {

            return GetCheckedItemsIgnoreRelation(_itemsSourceData);
        }

        /// <summary>
        /// 私有方法,忽略层次关系的情况下,获取选中项
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        private IList<TreeModel> GetCheckedItemsIgnoreRelation(IList<TreeModel> list)
        {
            IList<TreeModel> treeList = new List<TreeModel>();
            foreach (var tree in list)
            {
                if (tree.IsChecked==true)
                {
                    treeList.Add(tree);
                }
                foreach (var child in GetCheckedItemsIgnoreRelation(tree.Children))
                {
                    treeList.Add(child);
                }
            }
            return treeList;
        }

        /// <summary>
        /// 选中所有子项菜单事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuSelectAllChild_Click(object sender, RoutedEventArgs e)
        {
            if (tvZsmTree.SelectedItem != null)
            {
                TreeModel tree = (TreeModel)tvZsmTree.SelectedItem;
                tree.IsChecked = true;
                tree.SetChildrenChecked(true);
            }
        }

        /// <summary>
        /// 全部展开菜单事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuExpandAll_Click(object sender, RoutedEventArgs e)
        {
            if (tvZsmTree.ItemsSource!=null)
            {
                foreach (TreeModel tree in tvZsmTree.ItemsSource)
                {
                    tree.IsExpanded = true;
                    tree.SetChildrenExpanded(true);
                }
            }
        }

        /// <summary>
        /// 全部折叠菜单事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuUnExpandAll_Click(object sender, RoutedEventArgs e)
        {
            if (tvZsmTree.ItemsSource != null)
            {
                foreach (TreeModel tree in tvZsmTree.ItemsSource)
                {
                    tree.IsExpanded = false;
                    tree.SetChildrenExpanded(false);
                }
            }
        }

        /// <summary>
        /// 全部选中事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuSelectAll_Click(object sender, RoutedEventArgs e)
        {
            if (tvZsmTree.ItemsSource != null)
            {
                foreach (TreeModel tree in tvZsmTree.ItemsSource)
                {
                    tree.IsChecked = true;
                    tree.SetChildrenChecked(true);
                }
            }
        }

        /// <summary>
        /// 全部取消选中
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuUnSelectAll_Click(object sender, RoutedEventArgs e)
        {
            if (tvZsmTree.ItemsSource != null)
            {
                foreach (TreeModel tree in tvZsmTree.ItemsSource)
                {
                    tree.IsChecked = false;
                    tree.SetChildrenChecked(false);
                }
            }
        }

        /// <summary>
        /// 鼠标右键事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TreeViewItem_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            TreeViewItem item = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
            if (item != null)
            {
                item.Focus();
                e.Handled = true;
            }
        }
        static DependencyObject VisualUpwardSearch<T>(DependencyObject source)
        {
            while (source != null && source.GetType() != typeof(T))
                source = VisualTreeHelper.GetParent(source);

            return source;
        }

       
        private void treeNode_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = VisualUpwardSearch<TreeViewItem>(e.OriginalSource as DependencyObject) as TreeViewItem;
            if (item != null)
            {
                item.Focus();
                if (tvZsmTree.SelectedItem != null)
                {
                    TreeModel tree = (TreeModel)tvZsmTree.SelectedItem;
                    tree.SetChildrenChecked();
                    tree.SetParentChecked();
                }
                e.Handled = true;
            }  
        }

    }
}



上一篇:多线程域名查询工具

下一篇:个人代码全部开源


4 评论

   12/3/2016 3:55:31 PM     120.85.244....

结果发给客户方法恢复方法


   2/1/2016 9:42:06 PM     120.25.144....

正急需,请帮发一份(405404410@qq.com)谢谢


   2/1/2016 9:38:10 PM     120.25.144....

有源码吗


   11/10/2015 3:15:14 PM     59.40.97....

有源码吗?请帮发一份(8316499@qq.com)谢谢


[1楼]    7/13/2015 3:57:46 PM     58.58.185....

不错,mark


查看所有评论

给个评论吧