前言
本案例使用的 TreeListControl 是 DevExpress 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)