WPF中使用ListView封装组合控件TreeView+DataGrid-粉丝专栏

        wpf的功能非常强大,很多控件都是原生的,但是要使用TreeView+DataGrid的组合,就需要我们自己去封装实现。

我们需要的效果如图所示:

这2个图都是第三方控件自带的,并且都是收费使用。

现在我们就用原生的控件进行封装一个。

本文源码效果截图,(搞了好几天,的确有难度,所以源码也收费,便宜,赚点辛苦费)

功能如上图所示, 目前基本上把常用的样式都实现了,购买源码后,可以自行修改样式。

首先说明一下,实现上面的效果,有3种方法

第一种:技术的选择是TreeView。

WPF中使用TreeView封装组合控件TreeView+DataGrid-粉丝专栏-CSDN博客

第二种:技术的选择是DataGrid。

WPF中使用DataGrid封装组合控件TreeView+DataGrid-粉丝专栏-CSDN博客

第三种:技术的选择是ListView。(也就是本文的演示)

本文演示的是ListView的实现。

1.首先建立一个wpf程序

2.封装TreeList.cs



namespace ListView.TreeDataGrid
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Collections.Specialized;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    public class TreeList : ListView
    {
        #region Properties

        /// <summary>
        /// Internal collection of rows representing visible nodes, actually displayed in the ListView
        /// </summary>
        internal ObservableCollectionAdv<TreeNode> Rows
        {
            get;
            private set;
        }


        private ITreeModel _model;
        public ITreeModel Model
        {
            get { return _model; }
            set
            {
                if (_model != value)
                {
                    _model = value;
                    _root.Children.Clear();
                    Rows.Clear();
                    CreateChildrenNodes(_root);
                }
            }
        }

        private TreeNode _root;
        internal TreeNode Root
        {
            get { return _root; }
        }

        public ReadOnlyCollection<TreeNode> Nodes
        {
            get { return Root.Nodes; }
        }

        internal TreeNode PendingFocusNode
        {
            get;
            set;
        }

        public ICollection<TreeNode> SelectedNodes
        {
            get
            {
                return SelectedItems.Cast<TreeNode>().ToArray();
            }
        }

        public TreeNode SelectedNode
        {
            get
            {
                if (SelectedItems.Count > 0)
                    return SelectedItems[0] as TreeNode;
                else
                    return null;
            }
        }
        #endregion

        public TreeList()
        {
            Rows = new ObservableCollectionAdv<TreeNode>();
            _root = new TreeNode(this, null);
            _root.IsExpanded = true;
            ItemsSource = Rows;
            ItemContainerGenerator.StatusChanged += ItemContainerGeneratorStatusChanged;
        }

        void ItemContainerGeneratorStatusChanged(object sender, EventArgs e)
        {
            if (ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated && PendingFocusNode != null)
            {
                var item = ItemContainerGenerator.ContainerFromItem(PendingFocusNode) as TreeListItem;
                if (item != null)
                    item.Focus();
                PendingFocusNode = null;
            }
        }

        protected override DependencyObject GetContainerForItemOverride()
        {
            return new TreeListItem();
        }

        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            return item is TreeListItem;
        }

        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            var ti = element as TreeListItem;
            var node = item as TreeNode;
            if (ti != null && node != null)
            {
                ti.Node = item as TreeNode;
                base.PrepareContainerForItemOverride(element, node.Tag);
            }
        }

        internal void SetIsExpanded(TreeNode node, bool value)
        {
            if (value)
            {
                if (!node.IsExpandedOnce)
                {
                    node.IsExpandedOnce = true;
                    node.AssignIsExpanded(value);
                    CreateChildrenNodes(node);
                }
                else
                {
                    node.AssignIsExpanded(value);
                    CreateChildrenRows(node);
                }
            }
            else
            {
                DropChildrenRows(node, false);
                node.AssignIsExpanded(value);
            }
        }

        internal void CreateChildrenNodes(TreeNode node)
        {
            var children = GetChildren(node);
            if (children != null)
            {
                int rowIndex = Rows.IndexOf(node);
                node.ChildrenSource = children as INotifyCollectionChanged;
                foreach (object obj in children)
                {
                    TreeNode child = new TreeNode(this, obj);
                    child.HasChildren = HasChildren(child);
                    node.Children.Add(child);
                }
                Rows.InsertRange(rowIndex + 1, node.Children.ToArray());
            }
        }

        private void CreateChildrenRows(TreeNode node)
        {
            int index = Rows.IndexOf(node);
            if (index >= 0 || node == _root) // ignore invisible nodes
            {
                var nodes = node.AllVisibleChildren.ToArray();
                Rows.InsertRange(index + 1, nodes);
            }
        }

        internal void DropChildrenRows(TreeNode node, bool removeParent)
        {
            int start = Rows.IndexOf(node);
            if (start >= 0 || node == _root) // ignore invisible nodes
            {
                int count = node.VisibleChildrenCount;
                if (removeParent)
                    count++;
                else
                    start++;
                Rows.RemoveRange(start, count);
            }
        }

        private IEnumerable GetChildren(TreeNode parent)
        {
            if (Model != null)
                return Model.GetChildren(parent.Tag);
            else
                return null;
        }

        private bool HasChildren(TreeNode parent)
        {
            if (parent == Root)
                return true;
            else if (Model != null)
                return Model.HasChildren(parent.Tag);
            else
                return false;
        }

        internal void InsertNewNode(TreeNode parent, object tag, int rowIndex, int index)
        {
            TreeNode node = new TreeNode(this, tag);
            if (index >= 0 && index < parent.Children.Count)
                parent.Children.Insert(index, node);
            else
            {
                index = parent.Children.Count;
                parent.Children.Add(node);
            }
            Rows.Insert(rowIndex + index + 1, node);
        }
    }
}

3.TreeStyles.xaml

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:local="clr-namespace:ListView.TreeDataGrid"
                    >
    <Style TargetType="{x:Type local:TreeListItem}">
        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
        <Style.Triggers>
            <!--隔行换色-->
            <Trigger Property="ListBox.AlternationIndex"  Value="0" >
                <Setter Property="Background" Value="#e7e7e7" />
            </Trigger>
            <Trigger Property="ListBox.AlternationIndex" Value="1" >
                <Setter Property="Background" Value="#f2f2f2" />
            </Trigger>

            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="Orange"/>
            </Trigger>

            <Trigger Property="IsSelected" Value="True">
                <!--选中的行颜色-->
                <Setter Property="Background" Value="Aqua"/>
            </Trigger>
        </Style.Triggers>
    </Style>

    <Style x:Key="TreeListItemNodeStyle" TargetType="{x:Type local:TreeListItem}" >
        <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
        <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <ContentPresenter Content="{TemplateBinding ContentControl.Content}" 
										  ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" 
										  HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" 
										  VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" 
										  SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
                    <ControlTemplate.Triggers>
                        <Trigger Property="UIElement.IsEnabled" Value="False">
                            <Setter Property="TextElement.Foreground">
                                <Setter.Value>
                                    <DynamicResource ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
                                </Setter.Value>
                            </Setter>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

4.最终源码实例

说明: 使用ListView封装的时候,难度最大,或者没有找到最好的方法实现。

 源码地址:

https://download.csdn.net/download/u012563853/89003276

本文来源:

WPF中使用ListView封装组合控件TreeView+DataGrid-粉丝专栏-CSDN博客

之前发布的作废,这是从老外的源码转换过来,并进行了扩展: 1>添加了水平和垂直网格线 2>添加了MVVM 3>添加了UI虚拟化的支持 4>支持自动填充列和最小宽度列 5>可绑定到Datatable,从而进行编辑操作 6>可装载海量数据,单层数据超过1W,瞬间完成,50W数据的滚动不卡顿 7>集成到ComboBox 8>有两个主题,一个有类似传统的有折叠连接线的主题,另一个是当下的。 个人比较喜欢有折叠连接线的,结构可以更清晰。 特别强调一点:在海量数据的情况下,如果虚拟化UI开启,在展开大量数据的情况下,执行滚动操作可能导致程序假死,原因不明,希望有人能找到原因,并能告知!另外对于系统自带的TreeView测试发现,如果展开第二层,该层数据量很大,拖放也很卡,但DataGrid却非常顺滑,怀疑微软对层次结构的UI虚拟化仍没做好。 另外就个人感觉在目前的电脑配置下,UI虚拟化可以显著提高数据的加载速度,但一旦数据已加载后,执行拖放操作时,顺滑程度远不于非虚拟化的情况,原因也很简单,只要内存还够用,UI已加载的拖放肯定比UI虚拟的拖放好。 这次针对之前的发布主要在于完全取消了虚拟化,因为虚拟化会导致在大数据下滚动的卡死,垂直网格线不随滚动条滚动等各种意外的问题。 另外与树有关的: WPF TreeView的横向排布风格20170722(带动画) http://download.csdn.net/detail/maiker/9907400 Email: wuyang26@live.cn
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

故里2130

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值