【问题标题】:Jumping from one DB row to another using Fast Reports使用快速报告从一个数据库行跳转到另一行
【发布时间】:2013-04-10 04:34:56
【问题描述】:

我正在使用 Delphi 16 和 Fast Reports 4.1。 Embarcadero Edidion。

粗略地说,我的问题是: 有没有办法强制 frxReport(连接到数据库)在报告生成期间跳过一些表条目?

我的意思可以解释如下:

假设我们有一个来自数据库的大表,其中包含 1000 个条目,并假设我们要生成一个包含该表中数据的报告,每个条目一页。 自然地,1000 页的报告处理起来有点多,所以我们需要一种方法来告诉 Fast Report 在准备报告时要使用表格的哪些条目。

要仅选择表中所有条目的子集,我使用 DBGrid,在其中设置 DBGrid.Options.dgMultiselect := True 并通过鼠标单击选择所需的行。然后我将选定的行复制到一个单独的 TClientDataSet(named TempDataSet) 组件中,之后我将当前报告的数据集设置为新创建的 TempDataSet ('frxDataSet1.DataSet := TempDataSet') 并启动准备报告。

这似乎没问题,但在我正在编程的应用程序的上下文中,这是不可接受的。所以回到原来的问题:有没有办法控制在“frxReport1.PrepareReport();”时必须访问“frxDBDataset1.DataSet”中的哪些条目;叫做 ?

谢谢

【问题讨论】:

  • 所有过滤和排序过程都应在将数据传递给报告之前进行。您可以使用 TClientDataSet 来存储过滤和排序的行(就像您已经做的那样):o)
  • 作为替代方案,您可以尝试 TfrxUserDataset
  • 问题是,我的一半应用程序假定 frxReport 的数据集是 TADOTable 类型。糟糕的应用程序设计,我猜。这就是为什么我正在寻找替代方案。那么是否可以在不将其与具体数据库表关联的情况下创建并填充数据 TADOTable 对象?我的意思是一种内存中的 TADOTable...

标签: delphi fastreport


【解决方案1】:

您可以使用以下任何一种方法:

过滤器

您的原始数据集(如果数据集能够过滤)。您通常会编写一个 a la SQL 过滤器,如下所示:

MyDataSet.Filter := 'MYCOLUMN in (''a'', ''b'', ''c'')';
MyDataSet.Filtered := True;

或者编写一个OnFilterRecord 事件来排除/包含您要在报告中显示的记录。

procedure TMyForm.MyDataSetFilterRecord(DataSet: TDataSet; var Accept: Boolean);
begin
  Accept := (DataSet.FieldByName('MYFIELD').AsInteger mod 7 = 0) or 
            (DataSet.FieldByName('MYOTHERFIELD').AsInteger in MySetOfValues);
end;

TFrxDBDataSet 事件

您还可以使用您通常用来将报表绑定到数据的TfrxDBDataSet 组件的事件。编写一个OnOpenOnFirstOnNextOnCheckEOF 事件处理程序,通过仅在您感兴趣的记录上“停止”来防止报告包含所有记录。

例如,如果你想打印每一行,你可以这样写:

procedure TMyForm.fdsEstPagosNext(Sender: TObject);
begin
  MyDataSet.Next; 
  MyDataSet.Next; //advance not one, but two records.
end;

【讨论】:

  • 好的,感谢您提供的信息丰富的回复。我会试一试的。
  • 我按照您的建议进行了操作,并且有效。我有另一个问题。检查表的每条记录(行)的最快方法是什么,无论它(当前记录)是否已被用户选择?我在想这样的事情:如果 ADOTable1.Bookmark 在 DBGrid1 的选定行书签列表中,则“DBGrid1.SelectedRows.Find(ADOTable1.Bookmark, index)”应该返回 true。然而,这似乎对我不起作用。当我调用 ADOTable1.Bookmark 时,它返回一个空数组(属性 Bookmark 是一个动态字节数组)。
  • 您应该首先阅读该属性的文档(通常包括示例),如果您仍有疑问,最好在网站上发布一个新问题。 ;)
【解决方案2】:

我的回答有点跑题了,参考你上一条评论,可以用 Adodatasets 作为 Memorytables

var
 I:Integer;
begin
  for I := 0 to AdoDataset1.FieldCount - 1 do
    begin
      Adodataset2.FieldDefs.Add(AdoDataset1.Fields[i].FieldName
                               ,AdoDataset1.Fields[i].Datatype
                               ,AdoDataset1.Fields[i].Size);
    end;
      Adodataset2.CreateDataSet;
                                // just as example     
  While not AdoDataset1.Eof do  // here would be you code for inserting selected Rows
    begin
     AdoDataset2.Append;
    for I := 0 to AdoDataset1.FieldCount - 1 do
      begin
        Adodataset2.Fields[i].Assign(Adodataset1.Fields[i]);
      end;
    AdoDataset2.Post;
    AdoDataset1.Next;
    end;

end;

【讨论】:

  • 我做了类似于你建议的事情,但它不起作用,因为我的应用程序的许多函数都有 TADOTable,而不是 TDataSet,作为输入参数。而且我需要在不重复代码的情况下使用这些功能。我尝试的解决方案是创建一个 TADOTable 的子类,其中包含一个 TClientDataSet 作为私有字段,并且“行为”像一个 TClientDataSet 但“看起来”像 TADOTable 。有点危险,但我不知道如何按照 jachguate 的建议进行过滤
猜你喜欢
  • 2016-12-23
  • 2013-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多