☰
DevExpress GridLookUpEdit多列检索实战:FindFilterColumns与ActiveFilterString全解析
2026/10/17 3:14:56 网站建设 项目流程

1. 先搞清楚GridLookUpEdit的检索到底是哪一层在做

1.1 弹窗里那个GridView才是主角

很多刚接触DevExpress的朋友会把GridLookUpEdit当成一个“升级版ComboBox”来用:设置DataSource、DisplayMember、ValueMember,弹窗列勾选一下,跑起来能选值,就以为完事了。等真正遇到多列检索的需求,才发现连应该去哪个属性里找开关都摸不着头脑。

GridLookUpEdit本身并不负责显示网格,它在弹出状态下展示的是一个GridView实例。这个网格视图才真正承载列渲染、高亮、筛选、排序这些能力。所以讨论“多列检索”,本质上是在讨论“GridView怎么按用户在编辑框里的输入去过滤行”,而不是GridLookUpEdit这个壳子本身有什么魔法。这个认知一旦建立,后面很多问题就顺了。

我习惯把GridLookUpEdit拆成三层看:

  • 外层是编辑框,负责接受用户输入、显示当前选中文本;
  • 中间层是RepositoryItemGridLookUpEdit,它是编辑框和弹窗视图之间的配置桥梁,很多关键开关都挂在这层;
  • 内层是GridView,负责下拉网格的列展示、过滤、排序、选中。

多列检索的大多数配置集中在“中间层+内层”之间。很多人直接在GridView外层到处找“多列搜索”字样,找不到,其实不是控件不支持,是入口位置没找对。

1.2 默认检索为什么只认一列

GridLookUpEdit会把自己编辑框里的文本同步给GridView的查找过滤机制,这套机制叫FindFilterText。它指的是:用户在编辑框输入的关键字,会被当作GridView查找框里的筛选词,再结合“允许查找的列集合”定位匹配行。

问题就出在这个“允许查找的列集合”上。默认情况下,GridView的OptionsFind.FindFilterColumns只包含一个字段,也就是你设置的DisplayMember。举个例子,我的弹窗里显示“编码、名称、规格”三列,DisplayMember设成“名称”,那么用户不管输入编码还是规格,结果一律是“没有匹配项”。从用户视角看,就是这个下拉控件“只能按名称搜”,但明明列表里能看到编码和规格。

这个痛点几乎出现在我经手的每个物料选择、人员选择、客户选择界面。列表列越多,单列检索的问题越明显。有些项目甚至被业务方直接打回说“这个下拉框不好用,我要输入编号也搜不到”。实际上问题不在控件,而在配置上少做了几件事。

2. 方案一:打开GridView的FindFilterColumns

2.1 核心三处设置,一个都不能少

最省事的多列检索做法,是利用GridLookUpEdit内嵌GridView自带的查找过滤,把允许检索的列从“单个DisplayMember”扩展到“全部需要的字段”。

直接看代码:

var view = gridLookUpEdit.Properties.View; // 1. 把要参与检索的列都列在这里,分号分隔 view.OptionsFind.FindFilterColumns = "Code;Name;Spec;Spell"; // 2. 设置匹配方式为“包含”,输入的关键字出现在列的任何位置都能命中 view.OptionsFind.FindFilterMode = FindFilterMode.Contains; // 3. 让编辑框一有输入就立刻弹出下拉列表 gridLookUpEdit.Properties.ImmediatePopup = true;

第一行是最核心的。FindFilterColumns里写多少个字段,用户就能同时按多少个字段模糊查找。分号必须是英文分号,这是我在新手项目里见得最多的低级错误。

第二行的FindFilterMode决定了“模糊”的尺度。Contains是“包含即可”,只要关键字在字段任意位置出现就算命中。如果业务上希望“开头匹配”,可以改成StartsWith,比如按编码检索时输入“010”只想看010开头的。具体枚举值在不同版本里命名可能略有差异,但方向就是这两个。

第三行的ImmediatePopup可能很多人忽略。它不直接决定“能不能多列搜”,但决定了“好不好用”。如果不设,用户输入半天列表都不弹出来,要么需要手动点箭头,要么还要配置别的触发条件,体验很差。建议做这类即时检索界面时都打开它。

2.2 细节配置:属性编辑器也能做,但代码里要留注释

不一定非得写代码。在Visual Studio设计器里选中GridLookUpEdit,点开Properties属性,找到View下的OptionsFind,修改FindFilterColumns和FindFilterMode,效果完全一样。如果你是维护老项目,或者团队里有人习惯可视化配置,走属性编辑器也完全可以。

但我个人更推荐在代码里统一初始化,尤其当项目里多个GridLookUpEdit需要同样配置时。写一个公共方法:

public static void EnableMultiColumnSearch(GridLookUpEdit edit, string columns) { var view = edit.Properties.View; view.OptionsFind.FindFilterColumns = columns; view.OptionsFind.FindFilterMode = FindFilterMode.Contains; edit.Properties.ImmediatePopup = true; }

调用时传“Code;Name;Spec;Spell“这样的字符串,所有页面保持一致。后续如果在设计器里被人误改了,至少代码里还有个兜底。

还要提醒一个容易踩的坑:FindFilterColumns和AutoFilterRow是两套独立的东西。AutoFilterRow是GridView顶部单独露出一行筛选框,让用户在每个列头下自己输入过滤条件;FindFilter则是编辑框直接驱动的即时查找。两者可以在同一个View里共存,但一起开的时候很容易出现“筛选叠加”的奇怪效果,比如用户在AutoFilterRow里填了条件,又在编辑框里输入关键字,列表结果被两层条件同时限制,看起来就像“检索失灵”。实践中我强烈建议二选一。做GridLookUpEdit这种弹窗选择器,保留FindFilter就够,AutoFilterRow关掉更干净。

2.3 优点与局限

这套方案最大的优点就是快,配置少,不写搜索逻辑,基本零维护。它不需要你去监听TextChanged,不需要手动控制弹窗开关,GridView自己就把“编辑框输入→查找→刷新列表→高亮命中行”的全链路跑完了。适合大部分常规的业务字段检索场景。

局限也很明显:它本质上还是“单关键字模糊匹配”。如果用户想输入“01 空调”同时用编码和名称两个维度去搜,或者想输入“K T”用拼音首字母的两个词去过滤,FindFilterColumns是做不到的。它只会把一个完整的关键字整体拿去匹配每一列,不会自动切词。

数据量特别大的场景也需要留个心眼。FindFilter机制虽然经过控件内部优化,但十万级以上行数的数据源,每敲一个字符都触发全列模糊匹配,性能还是会慢慢变得迟钝。真到那个量级,我会直接换数据源层的过滤,而不是让控件硬扛。这一点放到后面“常见坑”里细说。

3. 方案二:用ActiveFilterString做完全可控的动态过滤

3.1 什么时候必须换自定义方案

FindFilterColumns很好用,但它解决不了所有问题。做某个产品目录选择功能时,我遇到过这样一类需求:用户习惯输入“A01 显示”这种混合内容,希望编码包含A01、同时名称包含“显示”的记录出现在结果里;用户还希望在弹窗打开后,输入拼音首字母例如“XS”也能搜到“显示器”“显示屏”这类词。

这种“多关键字、多列、支持拼音助记”的需求,FindFilterColumns做不了。它不是配置不够细,而是模型就不支持分词。这时候就得绕开内置的FindFilter,改用GridView的ActiveFiltering能力。

原理并不复杂:GridView支持一个ActiveFilterString属性,可以接收一段DevExpress的筛选表达式。表达式一旦设置,视图就会按照表达式逐行过滤。我们要做的,就是监听GridLookUpEdit的文本变化,把编辑框内容实时拼成一段“多列OR组合”的筛选条件,再塞给ActiveFilterString。

这段逻辑听上去容易,实际落地时踩过的坑,我踩完才摸清楚。下面直接给可用骨架:

private void gridLookUpEdit_TextChanged(object sender, EventArgs e) { // 用Timer做防抖,避免每个字符都触发过滤 filterTimer.Stop(); filterTimer.Start(); } private void filterTimer_Tick(object sender, EventArgs e) { filterTimer.Stop(); var edit = gridLookUpEdit; if (!edit.IsPopupOpen) { return; } var view = edit.Properties.View; string keyword = edit.Text.Trim(); if (string.IsNullOrEmpty(keyword)) { view.ActiveFilterString = string.Empty; return; } // 第一步:把单引号转义,防止用户在输入框里输入单引号破坏筛选表达式 keyword = keyword.Replace("'", "''"); // 第二步:按空格拆分成多个关键字,每个关键字都要匹配所有检索列 var parts = keyword.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var conditions = new List<string>(); string[] searchColumns = { "Code", "Name", "Spec" }; foreach (var part in parts) { var subConditions = new List<string>(); foreach (var col in searchColumns) { subConditions.Add($"[{col}] LIKE '%{part}%'"); } conditions.Add("(" + string.Join(" OR ", subConditions) + ")"); } view.ActiveFilterString = string.Join(" AND ", conditions); }

使用Timer防抖这一段,我一开始没写,结果每敲一个字母,GridView就全量重筛一遍。数据量只有几千行时还能忍,过万后明显发虚,弹窗卡顿、键盘上下选择滞后全来了。加个400毫秒左右的防抖后,整个体验立刻正常了。

3.2 几个必须处理好的副作用

这套方案的灵活性是拿“自己管状态”换来的。有几个副作用不处理干净,程序跑起来就会出现奇怪行为。

第一,ActiveFilterString和FindFilter不能同时用。只要你给view设置了ActiveFilterString,GridView的查找过滤就会和它叠加,甚至互相覆盖,结果可能不是你预期的。我踩过最典型的一次是:FindFilterColumns还保留着默认配置,同时我又在TextChanged里拼了ActiveFilterString,最后过滤条件变成“既要匹配FindFilter,又要匹配ActiveFilter”,用户输入明明能命中的数据却显示不出来。排查半天才意识到两套机制打架了。

我的处理方法是:凡是用ActiveFilterString方案的地方,直接把view.OptionsFind.FindFilterColumns清空,或者干脆设置FindFilterText为空。逻辑上强制走一条路。

第二,列名如果包含中文、空格、特殊字符,ActiveFilterString里必须用方括号括起来,比如[物料编码] LIKE '%xxx%'。不要省略,省略之后表达式解析会失败,或者匹配到一列错误的列名。

第三,不要在TextChanged里反复重建DataSource。有人为了搜索,每次FilterTimer触发就重新给GridLookUpEdit.DataSource赋值一个新DataTable,结果列表刷新后焦点丢失,用户按上下方向键没有任何反应。正确的做法是保持DataSource稳定,只改View的过滤条件。View自己会基于原数据源做行过滤,根本不需要重建数据源。

第四,编辑框内容为空时,要把ActiveFilterString设成string.Empty,否则上一次的过滤条件一直生效,列表显示不完整。这个坑很小,但很容易漏。

3.3 数据量再大一点怎么办

ActiveFilterString这条路在十万行以内、字段数量不超过五六个时,配合防抖完全够用。但如果要继续往上冲,我建议换思路:数据库端过滤,或者给数据源加一个“检索关键字冗余列”。

冗余列是我非常推荐的一种折中方案。做法是在SQL查询或者内存组装数据时,把参与检索的字段拼成一个大字符串,塞进一个专门用于检索的列里:

SELECT Code, Name, Spec, Code + ' ' + Name + ' ' + Spec AS SearchKey FROM Product

然后所有检索逻辑只需要匹配这一个SearchKey列:

view.ActiveFilterString = $"[SearchKey] LIKE '%{keyword}%'";

这样原本要跑五六个列的OR模糊匹配,变成了单列匹配。搜索性能提升非常明显,而且代码更简单。配合数据库端的全文检索索引,十万行、几十万行都不是问题。

我后来在很多项目的物料选择里都采用“冗余列+ActiveFilterString单列过滤”的组合,效果稳定,业务方也没再抱怨过“搜索慢”或者“搜不到”。

4. 实操中的常见坑与排查速查表

4.1 常见问题速查表

整理了这些年实际遇到的高频问题,直接做成表格方便排查。

现象可能原因排查与解决
设置了FindFilterColumns但检索仍只匹配一列FindFilterColumns没用英文分号分隔;或设置到了ListView而非GridView检查字符串是否为“Code;Name;Spec”这种英文分号格式;确认改的是Properties.View.OptionsFind,不是别的View
输入关键字后下拉列表不自动弹开ImmediatePopup没有打开设置gridLookUpEdit.Properties.ImmediatePopup = true
输入关键字后列表无数据,但下拉里明明有匹配行FindFilter与AutoFilterRow两套筛选叠加;或因ActiveFilterString残留导致双重过滤二选一:关闭AutoFilterRow,或确保没有同时设置ActiveFilterString/FIndFilterText
下拉列表能检索,但选中后编辑框显示的不是期望列DisplayMember设置不对检查GridLookUpEdit.Properties.DisplayMember是否指向需要显示的字段
TextChanged里动态过滤后键盘上下选择失效每次过滤重建了DataSource,焦点被重置不要重建DataSource,只修改view.ActiveFilterString
输入包含单引号、百分号等特殊字符时报错或无条件拼ActiveFilterString时没有转义keyword.Replace("'", "''");百分号按需转义或说明业务限制
大数据量下检索卡顿每字符全量过滤、字段多、无防抖加Timer防抖;使用冗余SearchKey列;数据源端过滤

这张表不能替代实际调试,但能帮你把80%的“脑雾问题”快速收敛到具体环节。

4.2 中文、拼音、助记码检索怎么扩展

前面讲的方案一和方案二,其实都只解决了“字段检索”,不解决“中文用户想用拼音搜中文”的问题。如果你的业务里用户经常输入拼音首字母或者完整拼音,比如设备表里写“显示器”,用户想输“XS”或者“xianshiqi”搜到,该怎么办?

两种常见思路。

第一种,数据源维护拼音列。在数据库表里加一列Spell,存的就是每条记录的拼音首字母或者完整拼音:

-- 示例:设备表 Code Name Spell D001 显示器 XSQ D002 打印机 DYJ

检索时直接把这列加进FindFilterColumns,或者拼进ActiveFilterString的OR条件里。优点是逻辑简单直接,跟普通字段检索一模一样;缺点是需要维护拼音数据,新增记录时得同步生成。

第二种,查询时边生成边匹配。如果你用的是数据库查询,可以在SQL里用函数现场生成拼音缩写再匹配。但说实话,不同数据库的拼音函数差异大,SQL写起来又长又难维护。我更推荐在程序里提前算好,或者在数据入库时生成拼音存下来。除非项目数据量很小、字段很少,否则不要试图在UI层临时计算拼音。

我见过一个比较成熟的做法是:数据源同时保留中文名称、完整拼音、拼音首字母三个查询维度,把这三个字段用冗余SearchKey的方式拼好,配合ActiveFilterString做单列模糊。用户不管输入中文、全拼还是首字母,一列匹配全部搞定。这个方案在多个项目里验证过,业务方满意度很高。

4.3 一个小技巧:点击弹窗后重置过滤条件

自定义ActiveFilterString方案还会遇到一个体验细节:用户上次输入了“AB”,列表被过滤了,关闭弹窗再重新打开,GridLookUpEdit的编辑框里还留着“AB”,ActiveFilterString也还在,列表依旧是过滤后的状态。这看起来“记忆功能”挺好,但大多数业务下拉选择器希望每次打开时干净一些。

我的习惯是在Popup事件里做一次重置:

private void gridLookUpEdit_Popup(object sender, EventArgs e) { var view = gridLookUpEdit.Properties.View; view.ActiveFilterString = string.Empty; gridLookUpEdit.Text = string.Empty; }

这样做的好处是,每次弹窗都是全量列表,用户输入后才会重新过滤。如果你希望保留上次筛选状态,那就别加这个处理。哪种好,取决于产品和业务预期,没有绝对标准,但我会优先保证“可控”。

5. 两种方案怎么选:就按这三条标准来

很多人问我,到底用内置FindFilterColumns还是ActiveFilterString?我的判断标准非常直接:

  • 只要求“多列模糊包含”,不需要分词、不需要拼音联想,选FindFilterColumns。一条配置搞定,几乎零代码,后续维护成本最低。
  • 要求“多关键字分词”“拼音首字母”“自定义匹配逻辑”,直接上ActiveFilterString。它自由度最大,但你要对焦点管理、防抖、转义、筛选叠加这些副作用负责。
  • 数据量大到数万行以上,优先考虑数据源端过滤,配合View视图层的ActiveFilterString只保留一个简单的显示逻辑。不要在GridView上再做多列OR过滤这种硬扛事。

在我自己的项目里,常见选择框用方案一,复杂业务选择框用方案二,重数据场景全部走数据库端过滤。这样定下来之后,团队协作时也不容易出现两个人各自按自己习惯写的情况。

GridLookUpEdit这控件本身不难,难点全在对“哪一层负责检索”的理解上。把编辑框、Properties、GridView三层关系摸透了,再做多列检索、再做自定义过滤,心里就非常有数了。我后来不管换什么业务系统,只要看到GridLookUpEdit,第一反应就是先看View的OptionsFind和ActiveFilterString,思路清晰,问题也就少了一半。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询