You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi FMX ListView筛选后调用FDQuery.First出现空白及访问违规求助

解决ListView关联FDQuery搜索后调用First导致空白+访问违规的问题

兄弟,我之前做Delphi项目时也踩过类似的坑,你的问题核心大概率是搜索操作破坏了ListView项和FDQuery记录的关联关系,加上AccessoryObject引用了已失效的内存对象,导致调用Query.First时触发访问违规,同时ListView同步异常变空白。下面给你分步骤拆解解决办法:

1. 先排查搜索逻辑的数据集同步问题

你在搜索时是不是直接修改了FDQuery的SQL语句然后重新Open?这种操作会销毁原来的数据集记录对象,导致ListView项的AccessoryObject指向的旧记录已经被释放,后续遍历或调用First时就会触发内存访问违规。

优化方案:用Filter替代直接修改SQL

如果你的搜索是针对现有数据集的过滤,建议用FDQuery的内置Filter功能,而不是重新执行SQL:

// 搜索时的代码
FDQuery.DisableControls; // 先禁用控件绑定,避免ListView实时刷新出问题
try
  FDQuery.Filter := '字段名 LIKE ' + QuotedStr('%' + Edit_Search.Text + '%');
  FDQuery.Filtered := True;
finally
  FDQuery.EnableControls; // 恢复控件绑定
end;

这样数据集不会重新创建记录对象,原来的AccessoryObject引用的记录(如果是指向FDQuery的当前记录对象)还能保持有效,同时ListView也不会突然空白。

2. 重构AccessoryObject的存储方式

别直接把FDQuery的记录对象或字段指针存到AccessoryObject里!一旦数据集重新加载(比如搜索时重新Open),这些对象会被释放,后续访问就是非法内存操作。

正确做法:存储唯一标识(比如主键ID)

把记录的主键ID(整数或字符串)转成TObject存储,遍历的时候通过ID定位记录:

// 初始化ListView项时(比如FDQuery遍历填充ListView)
while not FDQuery.Eof do
begin
  var LItem := ListView1.Items.Add;
  LItem.Text := FDQuery.FieldByName('Name').AsString;
  // 存储主键ID,转成TObject类型
  LItem.AccessoryObject := TObject(FDQuery.FieldByName('ID').AsInteger);
  FDQuery.Next;
end;

// 流程结束后遍历检查标记记录
FDQuery.DisableControls;
try
  FDQuery.First;
  for var LItem in ListView1.Items do
  begin
    if LItem.Checked then // 假设用Checked标记,你用AccessoryObject的话调整判断逻辑
    begin
      var TargetID := Integer(LItem.AccessoryObject);
      // 通过ID定位到对应的记录
      if FDQuery.Locate('ID', TargetID, [loCaseInsensitive]) then
      begin
        // 获取你需要的信息
        ShowMessage('选中记录ID:' + IntToStr(TargetID) + ',名称:' + FDQuery.FieldByName('Name').AsString);
      end;
    end;
  end;
finally
  FDQuery.EnableControls;
end;

这种方式完全不依赖数据集的内存对象,哪怕搜索后数据集重新加载,只要主键ID不变,就能准确找到对应记录。

3. 搜索后重新同步ListView和FDQuery

如果你的搜索必须重新执行SQL(比如涉及联表查询,Filter满足不了),那搜索后一定要重新遍历FDQuery填充ListView,并重新设置每个项的AccessoryObject,同时操作时要包裹DisableControls和EnableControls:

// 搜索执行SQL的代码
FDQuery.DisableControls;
try
  FDQuery.SQL.Text := 'SELECT * FROM 表名 WHERE 字段名 LIKE ' + QuotedStr('%' + Edit_Search.Text + '%');
  FDQuery.Open;
  
  // 清空ListView并重新填充
  ListView1.Items.Clear;
  while not FDQuery.Eof do
  begin
    var LItem := ListView1.Items.Add;
    LItem.Text := FDQuery.FieldByName('Name').AsString;
    LItem.AccessoryObject := TObject(FDQuery.FieldByName('ID').AsInteger);
    FDQuery.Next;
  end;
finally
  FDQuery.EnableControls;
end;

这样搜索后ListView和FDQuery的记录是完全对应的,后续调用FDQuery.First也不会出现空白或访问违规。

4. 调试访问违规的小技巧

如果还是出问题,打开Delphi的调试器,触发访问违规时查看调用栈,看是哪一行代码出错——大概率是访问AccessoryObject时指向了无效内存,这时候就回到上面的第2点,把存储对象改成存储主键ID即可解决。

内容的提问来源于stack exchange,提问作者Tallys Ferrante

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 07:43:04