欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WPF一个完整的TreeView使用实例:(一)自定义控件样式+数据源绑定+动态添加父子节点

程序员文章站 2022-01-05 10:50:25
...

TreeView控件可在树结构中显示分层数据,其中的项可以展开和折叠。它可以包含多种类型的控件,如Button、Lable、Image等控件,可以通过绑定到数据源并使用HieratchicalDataTemplate对象来填充其树。可以修改默认ControlTemplate以使控件具有独特的外观。

这里举一个完整的TreeView实例来进行说明。本节主要实现自定义控件样式+数据源绑定+动态添加父子节点的功能。效果如下:

WPF一个完整的TreeView使用实例:(一)自定义控件样式+数据源绑定+动态添加父子节点

1、主窗体中加入TreeView控件

        <TreeView x:Name="treeView" Background="Transparent" MinHeight="280" Width="200"
                  ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Auto"
                  MouseDoubleClick="TreeView_MouseDoubleClick">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Path=ChildNodes}">
                    <TextBlock x:Name="showName" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,8,0,0" Height="26"
                               FontSize="{Binding Path=SetFontSize}" FontWeight="{Binding Path=SetFontWeight}" Text="{Binding Path=NodeName, Mode=TwoWay}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

由于我们的这个TreeView控件父子节点字体样式、背景颜色等不同,所以使用Binding的方式通过后台进行设置。

2、TreeView的Model:TreeViewNode.cs

    public class TreeViewNode : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }

        #region 属性字段
        private int id;
        /// <summary>
        /// 节点ID
        /// </summary>
        public int Id
        {
            get
            {
                return id;
            }
            set
            {
                id = value;
            }
        }

        private int parentId;
        /// <summary>
        /// 父节点ID
        /// </summary>
        public int ParentId
        {
            get
            {
                return parentId;
            }
            set
            {
                parentId = value;
            }
        }

        private string nodeName;
        /// <summary>
        /// 节点名称(最多六个字符)
        /// </summary>
        public string NodeName
        {
            get
            {
                return nodeName;
            }
            set
            {
                nodeName = value;
                if (nodeName.Length > 5)
                {
                    //非添加项考虑字符长度
                    if (this.isNodeAdd != true && this.isChildNodeAdd != true)
                    {
                        nodeName = nodeName.Substring(0, 5);
                    }
                }
                OnPropertyChanged("NodeName");
            }
        }

        private bool isChildNode;
        /// <summary>
        /// 是否是子节点
        /// </summary>
        public bool IsChildNode
        {
            get
            {
                return isChildNode;
            }
            set
            {
                isChildNode = value;
            }
        }

        private bool isNodeAdd;
        /// <summary>
        /// 是否添加节点
        /// </summary>
        public bool IsNodeAdd
        {
            get
            {
                return isNodeAdd;
            }
            set
            {
                isNodeAdd = value;
            }
        }

        private bool isChildNodeAdd;
        /// <summary>
        /// 是否是添加子节点
        /// </summary>
        public bool IsChildNodeAdd
        {
            get
            {
                return isChildNodeAdd;
            }
            set
            {
                isChildNodeAdd = value;
            }
        }

        private ObservableCollection<TreeViewNode> childNodes;
        /// <summary>
        /// 子节点数据
        /// </summary>
        public ObservableCollection<TreeViewNode> ChildNodes
        {
            get
            {
                if (childNodes == null)
                {
                    childNodes = new ObservableCollection<TreeViewNode>();
                    childNodes.CollectionChanged += new NotifyCollectionChangedEventHandler(OnMoreStuffChanged);
                }
                return childNodes;
            }
            set
            {
                childNodes = value;
            }
        }
        private void OnMoreStuffChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                TreeViewNode stuff = (TreeViewNode)e.NewItems[0];
                stuff.ParentId = this.Id;
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                TreeViewNode stuff = (TreeViewNode)e.OldItems[0];
                if (stuff.ParentId == this.Id)
                {
                    stuff.ParentId = 0;
                }
            }
        }

        #region 界面展示相关属性
        //根据节点类型设置Margin
        public string Margining
        {
            get
            {
                double padLeft;
                if (this.isChildNode == true || this.isNodeAdd == true)
                {
                    padLeft = 36;
                }
                else
                {
                    padLeft = 10;
                }
                return string.Format("{0},0,0,0", padLeft);
            }
        }

        //添加节点按钮是否展示
        public Visibility ShowAddButton
        {
            get
            {
                if (this.isChildNode == false && this.isNodeAdd == true && this.isChildNodeAdd == false)
                    return Visibility.Visible;
                else
                    return Visibility.Collapsed;
            }
        }

        //根据节点设置分隔线
        public string ShowBorderThickness
        {
            get
            {
                if (this.isChildNode == false && this.isChildNodeAdd == false)
                    return string.Format("0,1,0,0");
                else
                    return string.Format("0,0,0,0");
            }
        }

        //根据子父节点设置字体大小
        public int SetFontSize
        {
            get
            {
                if (this.isChildNode == true)
                    return 12;
                else
                    return 14;
            }
        }

        //根据子父节点设置字体宽度
        public string SetFontWeight
        {
            get
            {
                if (this.isChildNode == true)
                    return "Normal";
                else
                    return "Bold";
            }
        }

        //根据子父节点设置字体颜色
        public string SetForeground
        {
            get
            {
                if (this.isChildNode == true || this.isNodeAdd == true)
                    return "#999999";
                else
                    return "#000000";
            }
        }

        //根据子父节点设置背景颜色
        public string SetBackground
        {
            get
            {
                if (this.isChildNode == true || this.isNodeAdd == true)
                    return "#ffffff";
                else
                    return "#87CEEB";
            }
        }

        //节点是否展开
        public bool SetIsExpanded
        {
            get
            {
                if (this.isChildNode != true || this.isNodeAdd != true)
                    return false;
                else
                    return true;
            }
        }
        #endregion
        #endregion

        #region 构造函数
        public TreeViewNode()
        {
        }

        public TreeViewNode(int _id, int _parentId, bool _isChildNode, bool _isChildNodeAdd, bool _isNodeAdd, string _nodeName)
        {
            this.id = _id;
            this.parentId = _parentId;
            this.isChildNode = _isChildNode;
            this.isChildNodeAdd = _isChildNodeAdd;
            this.isNodeAdd = _isNodeAdd;
            this.nodeName = _nodeName;
        }
        #endregion
    }

3、TreeView样式

        <!--TreeView样式-->
        <Style TargetType="TreeViewItem">
            <Setter Property="Cursor" Value ="Hand"></Setter>
            <Setter Property="AllowDrop" Value="True"></Setter>
            <Setter Property="BorderBrush" Value="#d3d3d3"></Setter>
            <Setter Property="BorderThickness" Value="1"></Setter>
            <Setter Property="Foreground" Value="{Binding Path=SetForeground}"></Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate x:Name="Parent" TargetType="TreeViewItem">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="auto"/>
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <Border Grid.Row="0" x:Name="headBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{Binding Path=ShowBorderThickness}" 
                                    Tag="{Binding ElementName=PART_Header}" PreviewMouseDown="TreeView_MouseDown">
                                <ContentPresenter Name="PART_Header" ContentSource="Header" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="{Binding Path=Margining}"/>
                            </Border>
                            <ToggleButton x:Name="toggleBtn" Grid.Row="0" Margin="0,0,10,0" VerticalAlignment="Center" HorizontalAlignment="Right" Tag="{Binding ElementName=PART_Header}" Style="{DynamicResource ToggleButtonStyle}" IsChecked="{Binding Path=IsExpanded, RelativeSource={RelativeSource TemplatedParent}}" ClickMode="Press"/>
                            <Image x:Name="addImg" Grid.Row="0" Width="20" Height="20" Margin="10,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Fill"
                                   Source="Resourse/add_normal.png" Visibility="{Binding Path=ShowAddButton}"></Image>
                            <ItemsPresenter x:Name="ChildNode" Grid.Row="1"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasItems" Value="false">
                                <Setter TargetName="toggleBtn" Property="Visibility" Value="Hidden"/>
                            </Trigger>
                            <Trigger Property="HasItems" Value="true">
                                <Setter TargetName="toggleBtn" Property="Visibility" Value="Visible"/>
                            </Trigger>
                            <Trigger Property="IsExpanded" Value="false">
                                <Setter TargetName="ChildNode" Property="Visibility" Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="IsExpanded" Value="True">
                                <Setter TargetName="ChildNode" Property="Visibility" Value="Visible"/>
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="True"/>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="headBorder" Property="Background" Value="#0000FF"/>
                                <Setter Property="Foreground" Value="#ffffff"/>
                                <Setter TargetName="addImg" Property="Source" Value="Resourse/add_press.png"/>
                                <Setter Property="IsExpanded"  Value="{Binding Path=SetIsExpanded}"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="True"/>
                                    <Condition Property="IsMouseOver" Value="False"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="headBorder" Property="Background" Value="#0000FF" />
                                <Setter Property="Foreground" Value="#ffffff"/>
                                <Setter TargetName="addImg" Property="Source" Value="Resourse/add_press.png"/>
                                <Setter Property="IsExpanded"  Value="{Binding Path=SetIsExpanded}"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="False"/>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="headBorder" Property="Background" Value="#d3d3d3"></Setter>
                                <Setter Property="Foreground" Value="{Binding Path=SetForeground}"/>
                                <Setter TargetName="addImg" Property="Source" Value="Resourse/add_normal.png"/>
                                <Setter Property="IsExpanded" Value="False"/>
                            </MultiTrigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsSelected" Value="False"/>
                                    <Condition Property="IsMouseOver" Value="False"/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName="headBorder" Property="Background" Value="{Binding Path=SetBackground}" />
                                <Setter Property="Foreground" Value="{Binding Path=SetForeground}"/>
                                <Setter TargetName="addImg" Property="Source" Value="Resourse/add_normal.png"/>
                                <Setter Property="IsExpanded" Value="False"/>
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <!--展开折叠按钮样式-->
        <Style x:Key="ToggleButtonStyle" TargetType="ToggleButton">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Grid SnapsToDevicePixels="True">
                            <Image x:Name="unfold" Source="Resourse/unfold.png" HorizontalAlignment="Left" Stretch="None"></Image>
                            <Image x:Name="fold" Source="Resourse/fold.png" HorizontalAlignment="Left" Stretch="None"></Image>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="Visibility" TargetName="unfold" Value="Visible"/>
                                <Setter Property="Visibility"  TargetName="fold" Value="Collapsed"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="False">
                                <Setter Property="Visibility" TargetName="unfold" Value="Collapsed"/>
                                <Setter Property="Visibility" TargetName="fold" Value="Visible"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

4、交互逻辑

        private ObservableCollection<TreeViewNode> TreeViewNodeList = null;//数据源
        private TreeViewNode TopNode = null;//顶层节点
        private int TopNodeId = -1;//顶层节点编号
        private int TopNodeParentId = -999;//顶层节点父节点编号
        private int MaxNodeIndex = 999;//最大节点编号

        public MainWindow()
        {
            InitializeComponent();
            InitTreeView();
        }

        private void InitTreeView()
        {
            TreeViewNodeList = new ObservableCollection<TreeViewNode>();
            this.treeView.ItemsSource = TreeViewNodeList;

            //初始化顶层节点数据
            TopNode = new TreeViewNode();
            TopNode.Id = TopNodeId;
            TopNode.ParentId = TopNodeParentId;

            //加入添加节点项
            TreeViewNode emptyNode = new TreeViewNode(MaxNodeIndex, TopNodeId, false, false, true, "双击添加新节点");
            emptyNode.ChildNodes = new ObservableCollection<TreeViewNode>();

            TreeViewNodeList.Add(emptyNode);
            TopNode.ChildNodes.Add(emptyNode);
        }

        /// <summary>
        /// 设置折叠状态
        /// </summary>
        private void TreeView_MouseDown(object sender, MouseButtonEventArgs e)
        {
            TreeViewItem SelectedfItem = new TreeViewItem();
            if (sender.GetType() == typeof(ToggleButton))
            {
                ToggleButton btn = (ToggleButton)sender;
                System.Windows.Controls.ContentPresenter CP = (System.Windows.Controls.ContentPresenter)btn.Tag;
                SelectedfItem = (TreeViewItem)CP.TemplatedParent;
            }
            else if (sender.GetType() == typeof(Border))
            {
                Border btn = (Border)sender;
                System.Windows.Controls.ContentPresenter CP = (System.Windows.Controls.ContentPresenter)btn.Tag;
                SelectedfItem = (TreeViewItem)CP.TemplatedParent;
            }
            else if (e.Source.GetType() == typeof(TreeViewItem))
            {
                SelectedfItem = (TreeViewItem)sender;
            }
            
            if (SelectedfItem == null || ((TreeViewNode)SelectedfItem.DataContext).IsChildNode || ((TreeViewNode)SelectedfItem.DataContext).IsNodeAdd)
            {
                SelectedfItem.IsExpanded = SelectedfItem.IsExpanded == true ? false : true;
            }
        }

        /// <summary>
        /// 双击添加节点
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TreeView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            TreeViewNode selectNode = (TreeViewNode)this.treeView.SelectedItem;

            if (selectNode == null)
                return;

            if (selectNode.IsNodeAdd)//双击添加节点
            {
                AddNode();
            }
            if (selectNode.IsChildNodeAdd)//双击添加子节点
            {
                AddAction();
            }
        }

        /// <summary>
        /// 添加节点
        /// </summary>
        private void AddNode()
        {
            TreeViewNode selectedItem = (TreeViewNode)this.treeView.SelectedItem;
            int index = GetNodeIndex(TopNode.ChildNodes, selectedItem);

            //添加新节点
            int nodeIndex = 1;
            string newNodeName = "节点" + nodeIndex;
            while (!CheckNodeNameAdd(TopNode, newNodeName))
            {
                nodeIndex++;
                newNodeName = "节点" + nodeIndex;
            }
            TreeViewNode newNode = new TreeViewNode(TreeViewNodeList.Count -1, -1, false, false, false, newNodeName);
            //添加子节点
            TreeViewNode childNode = new TreeViewNode(0, newNode.Id, true, false, false, "子节点1");
            newNode.ChildNodes.Add(childNode);
            //添加子节点Bottom项
            TreeViewNode actionNodeEmpty = new TreeViewNode(MaxNodeIndex, newNode.Id, true, true, false, "双击添加子节点");
            newNode.ChildNodes.Add(actionNodeEmpty);

            TopNode.ChildNodes.Insert(index, newNode);
            TreeViewNodeList.Insert(index, newNode);
        }

        /// <summary>
        /// 添加子节点
        /// </summary>
        private void AddAction()
        {
            TreeViewNode selectedItem = (TreeViewNode)this.treeView.SelectedItem;
            TreeViewNode selectParentNode = TopNode.ChildNodes[selectedItem.ParentId];
            int index = GetNodeIndex(selectParentNode.ChildNodes, selectedItem);

            //添加子节点
            int nodeIndex = 1;
            string newNodeName = "子节点" + nodeIndex;
            while (!CheckNodeNameAdd(selectParentNode, newNodeName))
            {
                nodeIndex++;
                newNodeName = "子节点" + nodeIndex;
            }

            TreeViewNode childNode = new TreeViewNode(selectParentNode.ChildNodes.Count - 1, selectedItem.ParentId, true, false, false, newNodeName);
            selectParentNode.ChildNodes.Insert(index, childNode);
        }

        /// <summary>
        /// 获取相应节点的编号
        /// </summary>
        private int GetNodeIndex(ObservableCollection<TreeViewNode> nodeList, TreeViewNode targetNode)
        {
            if (nodeList == null || nodeList.Count <= 0)
                return 0;
            for (int i = 0; i < nodeList.Count; i++)
            {
                if (nodeList[i].Equals(targetNode))
                    return i;
            }
            return 0;
        }

        /// <summary>
        /// 节点重名检查
        /// </summary>
        private bool CheckNodeNameAdd(TreeViewNode node, string name)
        {
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                if (node.ChildNodes[i].NodeName == name)
                {
                    return false;
                }
            }
            return true;
        }

 

相关标签: WPF WPF TreeView