cosky 发表于 2026-3-1 16:42:11

扩展DevExpress GridControl实现隐藏列筛选功能

本帖最后由 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方法 —— 该方法决定了哪些列参与筛选。
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 组件(即使暂时不需要自定义这些组件,也需适配创建,保证架构完整性)。
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 工具箱拖拽使用(控件拖拽用户狂喜)。
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Registrator;
using System.ComponentModel;

// 使控件在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. 辅助组件:适配默认实现
以下三个类为适配架构的辅助类,无需修改核心逻辑,仅需继承对应基类即可:
// 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 (下载后可自行升级到更高版本)

devstudy 发表于 2026-3-20 23:11:15

厉害 string items = checkedComboBoxEdit1.Properties.GetCheckedItems().ToString();             List<string> checkItems = new List<string>();            if (items.Contains(","))             {               string[] itemValue = items.Split(',');               foreach (string i in itemValue)               {                     checkItems.Add(i);               }             }            if (checkItems != null)             {               if (checkItems.Count() == checkedComboBoxEdit1.Properties.Items.Count)               {                     e.DisplayText = "ALL";               }             }
页: [1]
查看完整版本: 扩展DevExpress GridControl实现隐藏列筛选功能