WPF TreeListControl 使用技巧记录

182 阅读4分钟

前言

本案例使用的 TreeListControlDevExpress WPF中的控件,依赖于DevExpress.Xpf.Grid.xxx。本文主要用于记录在使用过程中的一些小技巧。

筛选后的结果如何保持树形层级关系

在实际使用过程中,我们将数据源以树形结果呈现,当数据量较大时,我们又希望开启筛选功能。树形结构中,不同层级间的筛选结果可能出现重复,如果简单以列表形式呈现,用户可能无法区分筛选出来的重复结果应该使用哪个(些)?此时,保持层级结果的呈现方式就尤为重要啦。 下面是使用案例的关键代码:

Window.xmal

<Window x:Class="ChildNodesSelector.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
        xmlns:local="clr-namespace:ChildNodesSelector"
        Title="MainWindow" Height="450" Width="700">
    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    <Window.Resources>
        <local:CustomChildrenSelector x:Key="childrenSelector"/>
    </Window.Resources>
    <Grid>
        <dxg:TreeListControl ItemsSource="{Binding DataItems}" AutoGenerateColumns="AddNew" EnableSmartColumnsGeneration="True">
            <dxg:TreeListControl.View>
                <dxg:TreeListView 
                                  SearchPanelAllowFilter="True"
                                  ExpandNodesOnFiltering ="True"
                                  KeyFieldName="Id"
                                  ParentFieldName="ParentId"
                                  FilterMode ="Extended"
                                  ShowSearchPanelMode="Always"/>
            </dxg:TreeListControl.View>
        </dxg:TreeListControl>
    </Grid>
</Window>

注: 这里的关键属性为:FilterMode ="Extended",用于控制筛选结果以树形结构呈现。另外ExpandNodesOnFiltering ="True"控制筛选后是否自动展开筛选结构的层级结构。

ViewModel.cs

using System;
using System.Collections.ObjectModel;

namespace ChildNodesSelector
{
    public class ViewModel
    {
        public ObservableCollection<ProjectObject> DataItems { get; set; }

        public ViewModel()
        {
            DataItems = InitData();
        }

        private ObservableCollection<ProjectObject> InitData()
        {
            ObservableCollection<ProjectObject> projects = new ObservableCollection<ProjectObject>();
            int size = 20;
            var rand = new Random((int)DateTime.Now.Ticks);
            string[] executors = { "Mcfadyen Ball", "Kaiden Savastano", "Carmine Then", "Seto Kober", "Manley Difrancesco", "Clint Mary" };
            string[] states = { "Completed", "In progress", "Not started", };
            var randomExe = new Random(5);
            for (var i = 0; i < size; i++)
            {
                projects.Add(new ProjectObject()
                {
                    Name = string.Format("Project: Betaron {0}", i + 1),
                    Id = $"{i + 1}",
                    ParentId = $"{rand.Next(projects.Count)}",
                    Executor = executors[randomExe.Next(0, 5)],
                    State = states[randomExe.Next(0, 2)],
                    Projects = projects,
                });
            }

            //ProjectObject betaronProject = new ProjectObject() { Name = "Project: Betaron", Stages = new ObservableCollection<ProjectStage>() };
            //ProjectObject stantoneProject = new ProjectObject() { Name = "Project: Stanton", Stages = new ObservableCollection<ProjectStage>() };

            //InitBetaronProjectData(betaronProject);
            //InitStantoneProjectData(stantoneProject);

            //projects.Add(betaronProject);
            //projects.Add(stantoneProject);

            return projects;
        }

        void InitBetaronProjectData(ProjectObject betaronProject)
        {
            betaronProject.Executor = "Mcfadyen Ball";
            ProjectStage stage21 = new ProjectStage() { Name = "Information Gathering", Executor = "Kaiden Savastano", Tasks = new ObservableCollection<StageTask>() };

            stage21.Tasks.Add(new StageTask() { Name = "Market research", Executor = "Carmine Then", StartDate = new DateTime(2011, 10, 1), EndDate = new DateTime(2011, 10, 5), State = "Completed" });
            stage21.Tasks.Add(new StageTask() { Name = "Making specification", Executor = "Seto Kober", StartDate = new DateTime(2011, 10, 5), EndDate = new DateTime(2011, 10, 10), State = "In progress" });
            ProjectStage stage22 = new ProjectStage() { Name = "Planning", Executor = "Manley Difrancesco", Tasks = new ObservableCollection<StageTask>() };

            stage22.Tasks.Add(new StageTask() { Name = "Documentation", Executor = "Martez Gollin", StartDate = new DateTime(2011, 10, 15), EndDate = new DateTime(2011, 10, 16), State = "Not started" });
            ProjectStage stage23 = new ProjectStage() { Name = "Design", Executor = "Clint Mary", Tasks = new ObservableCollection<StageTask>() };

            stage23.Tasks.Add(new StageTask() { Name = "Design of a web pages", Executor = "Gasper Hartsell", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "Not started" });
            stage23.Tasks.Add(new StageTask() { Name = "Pages layout", Executor = "Shirish Huminski", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "Not started" });
            ProjectStage stage24 = new ProjectStage() { Name = "Development", Executor = "Edwin Thone", Tasks = new ObservableCollection<StageTask>() };

            stage24.Tasks.Add(new StageTask() { Name = "Design", Executor = "Zarko Knill", StartDate = new DateTime(2011, 10, 27), EndDate = new DateTime(2011, 10, 28), State = "Not started" });
            stage24.Tasks.Add(new StageTask() { Name = "Coding", Executor = "Harley Kirckof", StartDate = new DateTime(2011, 10, 29), EndDate = new DateTime(2011, 10, 30), State = "Not started" });
            ProjectStage stage25 = new ProjectStage() { Name = "Testing and Delivery", Executor = "Boucher Hislop", Tasks = new ObservableCollection<StageTask>() };

            stage25.Tasks.Add(new StageTask() { Name = "Testing", Executor = "Sarah Ragas", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "Not started" });
            stage25.Tasks.Add(new StageTask() { Name = "Content", Executor = "Rashid Terinoni", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "Not started" });

            betaronProject.Stages.Add(stage21);
            betaronProject.Stages.Add(stage22);
            betaronProject.Stages.Add(stage23);
            betaronProject.Stages.Add(stage24);
            betaronProject.Stages.Add(stage25);
        }

        void InitStantoneProjectData(ProjectObject stantoneProject)
        {
            stantoneProject.Executor = "Ruben Ackerman";
            ProjectStage stage11 = new ProjectStage() { Name = "Information Gathering", Executor = "Huyen Trinklein", Tasks = new ObservableCollection<StageTask>() };

            stage11.Tasks.Add(new StageTask() { Name = "Market research", Executor = "Tanner Crittendon", StartDate = new DateTime(2011, 10, 1), EndDate = new DateTime(2011, 10, 5), State = "Completed" });
            stage11.Tasks.Add(new StageTask() { Name = "Making specification", Executor = "Carmine Then", StartDate = new DateTime(2011, 10, 5), EndDate = new DateTime(2011, 10, 10), State = "Completed" });
            ProjectStage stage12 = new ProjectStage() { Name = "Planning", Executor = "Alfredo Sookoo", Tasks = new ObservableCollection<StageTask>() };

            stage12.Tasks.Add(new StageTask() { Name = "Documentation", Executor = "Gorf Wobbe", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "Completed" });

            ProjectStage stage13 = new ProjectStage() { Name = "Design", Executor = "Saphire Plump", Tasks = new ObservableCollection<StageTask>() };

            stage13.Tasks.Add(new StageTask() { Name = "Design of a web pages", Executor = "Dominic Minden", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "In progress" });
            stage13.Tasks.Add(new StageTask() { Name = "Pages layout", Executor = "Pinkerton Trezise", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "In progress" });
            ProjectStage stage14 = new ProjectStage() { Name = "Development", Executor = "Lauren Partain", Tasks = new ObservableCollection<StageTask>() };

            stage14.Tasks.Add(new StageTask() { Name = "Design", Executor = "Delilah Beamer", StartDate = new DateTime(2011, 10, 23), EndDate = new DateTime(2011, 10, 24), State = "In progress" });
            stage14.Tasks.Add(new StageTask() { Name = "Coding", Executor = "Dunaway Dupriest", StartDate = new DateTime(2011, 10, 25), EndDate = new DateTime(2011, 10, 26), State = "Not started" });
            ProjectStage stage15 = new ProjectStage() { Name = "Testing and Delivery", Executor = "Christos Arrant", Tasks = new ObservableCollection<StageTask>() };

            stage15.Tasks.Add(new StageTask() { Name = "Testing", Executor = "Grice Ohora", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "Not started" });
            stage15.Tasks.Add(new StageTask() { Name = "Content", Executor = "Christos Arrant", StartDate = new DateTime(2011, 10, 13), EndDate = new DateTime(2011, 10, 14), State = "Not started" });

            stantoneProject.Stages.Add(stage11);
            stantoneProject.Stages.Add(stage12);
            stantoneProject.Stages.Add(stage13);
            stantoneProject.Stages.Add(stage14);
            stantoneProject.Stages.Add(stage15);
        }
    }
}

DataModel.cs

using System;
using System.Collections.ObjectModel;

namespace ChildNodesSelector
{
    public class BaseObject
    {
        public string Name { get; set; }
        public string Executor { get; set; }
        public string State { get; set; }
        public string Id { get; set; }
        public string ParentId { get; set; }

        public ObservableCollection<ProjectObject> Projects { get; set; }

        public BaseObject()
        {
            Projects = new ObservableCollection<ProjectObject>();
        }
    }

    public class ProjectObject : BaseObject
    {
        public ObservableCollection<ProjectStage> Stages { get; set; }
    }

    public class ProjectStage : BaseObject
    {
        public ObservableCollection<StageTask> Tasks { get; set; }
    }

    public class StageTask : BaseObject
    {
        public DateTime StartDate { get; set; }
        public DateTime EndDate { get; set; }
    }
}

ChildNodesSelector.cs

using DevExpress.Xpf.Grid;
using System.Collections;
using System.Collections.ObjectModel;
using System.Linq;

namespace ChildNodesSelector
{
    public class CustomChildrenSelector : IChildNodesSelector
    {
        public IEnumerable SelectChildren(object item)
        {
            if (item is ProjectStage)
                return (item as ProjectStage).Tasks;
            else if (item is ProjectObject)
                return (item as ProjectObject).Stages;
            return null;
        }
    }
}

源码参考地址: RunWangusst/DevExpressTreeListControlDemo: TreeListControl demo (github.com)