delphi 在cxgrid表中设置下拉菜单,以及多表查询时,如何更新数据(TFDUpdateSQL)
我写的博客内容,都是在实际生产中遇到的问题,针对性很强,记录下来有两个目的,一是当成笔记,二是丰富Delphi的网上资料,让遇到相同问题的朋友,少走弯路.
如下图,我希望所属仓库,供应商,物料用途这三个字段,我希望做成下拉菜单的形式给用户选择,

但因为这个表格是多表联合查询得出来的结果,如果直接交给cxgrid自动处理的话,会产生错误

首先,先来实现下拉菜单的效果.我们以物料用途字段为例进行说明.
1.选中 物料用途 字段,在属性面板中设置properties=LookupComboBox,此时properties属性的左边会多出来一个大于号,像这样
2.点开这个大于号,设置ListSource为一个我们事先准备好的数据集
.
3.在ListColumns中设置下拉菜单的显示列,可以同时显示多列,同时要设置KeyFieldNames属性,比如你在ListColumns中设置了 用途与单位 这两列,那么对应的,你要在KeyFieldNames中设置为: 用途;单位 ,列与列之间用分号隔开,这里是可以手输的.如果KeyFieldNames与ListColumns不对应,就会出错.


但切记要设置好列宽.下面几个参数要留意一下,它们都在properties属性中设置:

DropDownAutoSize: 使下拉菜单的宽度等于屏幕的宽度.除非你要显示的列非常多,否则不勾选.效果如下

DropDownRows:下拉菜单中最多显示多少行,这里很好理解,就不出图了
DropDownSizeable:使下拉菜单可以通过右下角进行拉伸,改变下拉菜单的大小,同时在左下角提供一个关闭下拉菜单的按钮

说完了下拉菜单设置,现在来说说怎么实现更新.这里先声明,我的方法可能并不是最优解,但能解决实际问题,欢迎大佬指点
这里要用到properties当中的两个事件OnInitPopup事件与OnValidate事件

OnInitPopup事件是在下拉菜单弹出之前,更新它的用途,因为每一行的数据可能不同,用途要通过查询才能返回对应的历史用途.
另外因为我使用的是Access数据库,SQL语句请按自己的对应的数据库语言来写
procedure T申领申购报废清单.T1用途PropertiesInitPopup(Sender: TObject); var codeText: string; begin codeText := DM.FDQ报废池.FieldByName('物料代码').AsString; //取得当前行的物料代码,然后再以此向数据库查询它的历史用途 with DM.FDQ用途 do //下拉菜单关联着这个查询的数据集,所以我们只要更新这个查询就可以了. begin close; SQL.Text := 'select distinct 用途 from(SELECT 用途 FROM 采购入仓记录 WHERE 物料代码=' + codeText.QuotedString + ' ORDER BY 入仓日期 DESC)'; Open(); end; end;
OnValidate事件:当焦点离开当前单元格时,触发此事件.同时会触发cxgrid的更新事件,但因为数据表是联合查询得来的,cxgrid会向一个不存在的表进行更新操作,所以导致出错
procedure T申领申购报废清单.T1用途PropertiesValidate(Sender: TObject; var DisplayValue: Variant; var ErrorText: TCaption; var Error: Boolean); var YT: string; begin YT := DisplayValue; //当前值,也就是用户点选的值
//这里你完全可以再次判断YT的数据是否合法,再决定更新与否 TV报废池.DataController.Cancel; //取消更新,避免出错. //重新定义更新内容,因为是临时的,所以用了一个公共的查询来执行更新操作 with DM.FD公共查询 do begin close; SQL.Text := 'update 报废池 set 用途=' + YT.QuotedString + ' where RecordID=' + dm.FDQ报废池.FieldByName('RecordID').asstring; ExecSQL; end; dm.FDQ报废池.Refresh; //更新数据集 end;
坑:


2025-11-19 更新.关于涉及到多表个数据表的联合查询,如何更新数据的问题.最优解是使用TFDUpdateSQL控件.使用方法如下:
1.假设联合查询为FDQuery1,TFDUpdateSQL控件为FDUpdateSQL1,首先需要把它们关联起来.请设置FDQuery1.UpdateObject属性为FDUpdateSQL1
2.设置FDUpdateSQL1的SQL语句.你可以在属性中设置(推荐),也可以在代码中设置

在代码是设置:
procedure TMaterialInfomation.SetupUpdateSQL; begin with FData.FDUpdateSQL1 do begin // 设置修改语句 ModifySQL.Text := 'UPDATE 物料信息 SET 物料名称 = :物料名称,规格型号 = :规格型号, 材质 = :材质,' + '客户料号 = :客户料号,启用状态 = :启用状态, 备注 = :备注,' + '助记码=:助记码,储位=:储位,单重 = :单重, 权重 = :权重 WHERE 物料ID = :物料ID'; // 设置插入语句(如果需要) InsertSQL.Text := 'INSERT INTO 物料信息 (物料名称, 规格型号, 材质, 客户料号, 启用状态, 备注, ' + '默认仓库, 助记码, 储位, 单位, 单重, 权重, 物料ID) ' + 'VALUES (:物料名称, :规格型号, :材质, :客户料号, :启用状态, :备注, ' + ':默认仓库, :助记码, :储位, :单位, :单重, :权重, :物料ID)'; // 设置删除语句(如果需要) DeleteSQL.Text := 'DELETE FROM 物料信息 WHERE 物料ID = :物料ID'; end; end;
3.使用方法.如果是在代码中设置的,需要在POST前调用 SetupUpdateSQL 过程,比如:
if FDQuery1.State in [dsEdit,dsInsert] then begin SetupUpdateSQL; FDQuery1.Post; end;

浙公网安备 33010602011771号