本帖最后由 cosky 于 2026-3-1 18:01 编辑
名词解释:
1、文中提到的GridControl均为DevExpress中的GridControl控件。
2、GridView为DevExpress中GridControl控件中的GridView视图。
一、前言:
在企业级应用开发中,数据表格(Grid)是最重要的UI组件之一。DevExpress的GridControl作为业界领先的网格控件,提供了丰富的功能和高度的可定制性。
其中我们经常需要对表格数据进行查询筛选操作。GridView提供了方便的接口函数,调用GridView.ApplyFindFilter(string filterText)方法即可快速对视图中的数据进行查找和筛选。
但是,GridControl中的筛选功能只能针对视图中已显示的列(GridColumn.Visible = true)进行筛选,隐藏列的无法进行查找和筛选。
这种设计在某些业务场景下显得不够灵活。
我们设想一下:有一个产品列表数据,包括:编号、产品名称、描述等字段。现在我们为了方便用户搜索增加了一个快速搜索字段,内容是由产品名称或者描述转换成拼音以及拼音首字母。这样用户敲几个拼音字母就可以方便的进行查找和筛选了。很显然这个和业务无关的快速搜索字段我们不希望它在视图中显示,但是GridControl中的设定又是隐藏的列无法被筛选(一根筋两头堵了属于是)
如图:
针对这个问题有人提交过issue,官方的回复是:“筛选隐藏的列?这很奇怪,也会让用户感到困扰!”。
emmm...看样子官方是不打算改进了。
别急,本文将详细介绍如何通过继承和扩展DevExpress的GridControl来实现这一高级功能,包括其背后的设计模式、架构原理以及具体的实现细节。
二、DevExpress GridControl 核心架构
在扩展前,先理解 GridControl 的插件式架构(基于策略模式 + 工厂模式),这是自定义扩展的基础:
DevExpress的GridControl采用了一套复杂的插件式架构:
GridControl
├── View (BaseView)
│ ├── ViewInfo (BaseViewInfo)
│ ├── Handler (BaseViewHandler)
│ └── Painter (BaseViewPainter)
└── InfoRegistrator (GridInfoRegistrator)
这种设计模式基于策略模式和工厂模式,每个组件各司其职:
- GridControl: 控件容器,管理视图生命周期
- View: 数据展示逻辑的核心,
- ViewInfo: 视图状态和布局信息
- Handler: 用户交互处理
- Painter: 视觉渲染
- InfoRegistrator: 视图类型的注册和创建
三、分步实现:自定义 GridControl 支持隐藏列筛选
1. 核心:CustomGridView(重写筛选列集合逻辑)
这是实现隐藏列筛选的核心类,关键是重写GetFindToColumnsCollection方法 —— 该方法决定了哪些列参与筛选。
[C#] 纯文本查看 复制代码 using DevExpress.XtraGrid.Views.Grid;
using System.Collections.Generic;
public class CustomGridView : GridView
{
// 无参构造函数
public CustomGridView() { }
// 带GridControl的构造函数(关联控件容器)
public CustomGridView(GridControl grid) : base(grid) { }
/// <summary>
/// 重写筛选列集合逻辑:添加隐藏列到筛选集合
/// </summary>
/// <returns>包含可见列+符合条件的隐藏列的筛选集合</returns>
protected override List<IDataColumnInfo> GetFindToColumnsCollection()
{
// 1. 先获取默认的可见列筛选集合
List<IDataColumnInfo> filterColumns = base.GetFindToColumnsCollection();
// 2. 遍历所有列,将「隐藏且未分组」的列加入筛选集合
foreach (GridColumn column in Columns)
{
// 条件说明:
// !column.Visible → 仅处理隐藏列
// column.GroupIndex == -1 → 排除分组列(避免重复处理)
if (!column.Visible && column.GroupIndex == -1)
{
// 为隐藏列创建筛选所需的列信息对象
filterColumns.Add(CreateIDataColumnInfoForFilter(column));
}
}
return filterColumns;
}
// 标识自定义视图名称(需与注册器一致)
protected override string ViewName => "CustomGridView";
}
核心逻辑解析:
- base.GetFindToColumnsCollection():先保留原有可见列的筛选逻辑,保证默认功能不受影响;
- 遍历所有列,筛选出「隐藏且未分组」的列,通过CreateIDataColumnInfoForFilter方法为其创建筛选所需的IDataColumnInfo对象,加入筛选集合;
- 排除分组列(GroupIndex == -1)是关键的性能优化,避免重复处理。
2. 视图注册器:CustomGridViewInfoRegistrator
这是 DevExpress 插件架构的「工厂类」,负责注册自定义视图,并创建配套的 ViewInfo/Handler/Painter 组件(即使暂时不需要自定义这些组件,也需适配创建,保证架构完整性)。
[C#] 纯文本查看 复制代码 using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Views.Base;
public class CustomGridViewInfoRegistrator : GridInfoRegistrator
{
// 自定义视图名称(需与CustomGridView的ViewName一致)
public override string ViewName => "CustomGridView";
// 创建自定义视图实例
public override BaseView CreateView(GridControl grid)
{
return new CustomGridView(grid);
}
// 创建视图信息对象(适配默认实现)
public override BaseViewInfo CreateViewInfo(BaseView view)
{
return new CustomGridViewInfo(view as CustomGridView);
}
// 创建交互处理对象(适配默认实现)
public override BaseViewHandler CreateHandler(BaseView view)
{
return new CustomGridViewHandler(view as CustomGridView);
}
// 创建渲染对象(适配默认实现)
public override BaseViewPainter CreatePainter(BaseView view)
{
return new CustomGridViewPainter(view as CustomGridView);
}
}
3. 自定义控件容器:CustomGridControl
将自定义视图注册到 GridControl,使其成为默认视图,同时支持 Visual Studio 工具箱拖拽使用(控件拖拽用户狂喜)。
[C#] 纯文本查看 复制代码 using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Registrator;
using System.ComponentModel;
[ToolboxItem(true)] // 使控件在VS工具箱中可见
public class CustomGridControl : GridControl
{
/// <summary>
/// 重写默认视图创建逻辑:使用自定义的CustomGridView
/// </summary>
protected override BaseView CreateDefaultView()
{
return CreateView("CustomGridView");
}
/// <summary>
/// 注册自定义视图类型到控件
/// </summary>
protected override void RegisterAvailableViewsCore(InfoCollection collection)
{
// 先注册默认视图,保证原有功能
base.RegisterAvailableViewsCore(collection);
// 添加自定义视图注册器
collection.Add(new CustomGridViewInfoRegistrator());
}
}
4. 辅助组件:适配默认实现
以下三个类为适配架构的辅助类,无需修改核心逻辑,仅需继承对应基类即可:
[C#] 纯文本查看 复制代码 // CustomGridViewInfo:视图信息类
using DevExpress.XtraGrid.Views.Grid.ViewInfo;
public class CustomGridViewInfo : GridViewInfo
{
public CustomGridViewInfo(CustomGridView view) : base(view) { }
}
// CustomGridViewHandler:交互处理类
using DevExpress.XtraGrid.Views.Grid.Handler;
public class CustomGridViewHandler : GridHandler
{
public CustomGridViewHandler(CustomGridView view) : base(view) { }
}
// CustomGridViewPainter:渲染类
using DevExpress.XtraGrid.Views.Grid.Painters;
public class CustomGridViewPainter : GridPainter
{
public CustomGridViewPainter(CustomGridView view) : base(view) { }
}
四、使用方式
- 编译上述代码生成控件库,在 VS 工具箱中找到CustomGridControl,拖拽到窗体;
- 像使用普通 GridControl 一样绑定数据,添加需要隐藏的「拼音检索列」并设置Visible = false;
- 调用CustomGridView.ApplyFindFilter("拼音关键词"),即可实现基于隐藏列的筛选。
五、性能与扩展建议
- 性能优化:若表格数据量极大(10 万 + 行),可在GetFindToColumnsCollection中增加列白名单,仅将需要筛选的隐藏列加入集合,避免遍历所有列;
- 扩展方向:可新增属性(如AllowHiddenColumnFilter),控制是否启用隐藏列筛选,提升控件灵活性;
- 兼容性:该实现基于 DevExpress 通用架构,适配 v18.2 + 版本(低版本需微调命名空间)。
总结
- 实现隐藏列筛选的核心是重写GridView.GetFindToColumnsCollection方法,将符合条件的隐藏列加入筛选集合;
- DevExpress GridControl 的插件式架构(策略 + 工厂模式)是扩展的基础,需通过 InfoRegistrator 注册自定义视图及配套组件;
- 扩展过程中需保留原有逻辑(调用 base 方法),仅增量修改核心逻辑,保证控件原有功能不受影响。
通过这种方式,我们既解决了业务痛点,又遵循了 DevExpress 的架构设计,实现了高可维护性的自定义扩展。最终看看效果吧
【完美】
完整方案下载:
Demo项目环境:
IDE:Visual Studio 2022
框架:.Net Framework 4.8 (代码兼容.net 10)
DevExpress:24.2.6 (下载后可自行升级到更高版本)
|