FastReport集粹(一)

unit ReportClass;

interface

uses CommonForm, SysUtils, Classes, Forms, Dialogs, DB, Controls, Grids, ComCtrls,
     FR_DBSet, FR_Class, Printers, FR_Utils, FR_Pars, DBGrids, IniFiles, Variants,
     TypInfo;

type
  TReport = class
  private
    FDatasetArray: array of TDataset;
    FRDataset: array of TfrDBDataset;

    FrmCommon: TfrmCommon; //设置窗体
    FReport: TfrReport; //报表对象
    FPage: TfrPage; //页面对象
    FVariants: TfrVariables; //报表的数据字典变量
    FVariantList: TStrings; //变量列表

    FGridFields: TStrings; //表格的字段=标签
    FGridWidths: TStrings; //表格的字段=宽度
    FGridRowHeight: integer; //表格行高
    FGrid: TCustomGrid; //表格名称

    FTitleMemo: TfrMemoView; //标题头文本框
    FTitleDate: TfrmemoView; //标题日期文本框
    FPrintReportDate: Boolean; //是否打印页头日期
    FOrientation: Boolean; //报表打印方向
    FPrintPageNumber: Boolean; //是否打印页码

    FHeaderFields: TStrings; //页头字段列表
    FDetailFields: TStrings; //明细字段列表
    FFooterFields: Tstrings; //页脚字段列表

    INIFile: TINIFile;
    FFileName: string; //报表的文件名
    FDetailSourceType: string; //报表数据源类型('TDBGrid'或'TDataset')
    FReportName: string; //在INI文件中小节的报表的名称
    procedure ShowData;
    function GetVariant: TStrings;
    procedure SetVariants(const Value: TStrings);
  protected
    function GetReportTitle: string;
    procedure SetReportTitle(value: string);

    function GetOrientation: Boolean;
    procedure SetOrientation(value: Boolean);

    function GetPrintReportDate: Boolean;
    procedure SetPrintReportDate(value: Boolean);

    function GetPrintPageNumber: Boolean;
    procedure SetPrintPageNumber(value: Boolean);

    function GetHeaderFields: TStrings;
    procedure SetHeaderFields(const Value: TStrings);

    function GetFooterFields: TStrings;
    procedure SetFooterFields(const Value: TStrings);
    function GetDetailFields: TStrings;
    procedure SetDetailFields(const Value: TStrings);

    function GetFileName: string;
    procedure SetFileName(const Value: string);
   
    function GetGrid: TCustomGrid;
    procedure SetGrid(const Grid: TCustomGrid);
  public
    constructor Create;
    destructor Destroy; override;

    procedure Preview; overload;
    procedure Preview(FileName: string); overload;
    procedure Design; overload;
    procedure Design(FileName: string); overload;

    procedure SetMemoView(MemoView: TfrMemoView; //设置文本框属性
                          Alignment: integer;    //Alignment:文本对齐方式,frtaCenter表示居中对齐
                          FrameTyp: word);       //FrameTyp:边框样式,0:无边框;15:有边框
    function FindBandView(BandType: TfrBandType): TfrBandView; //根据BandView类型返回TfrBandview
    function FindMemoView(Text: string; var CompNo: integer): TfrMemoView; //根据MemoView的文本返回TfrMemoView和序号
    function ShowFieldListForm: Boolean;

    procedure SaveFieldList(ReportName: string); //存储字段列表到报表配置文件中
    procedure LoadFieldList(ReportName: string); //从配置文件中调用字段列表

    procedure SetDataset(Datasets: array of TDataset);
  published
    property Title: string read GetReportTitle write SetReportTitle; //报表标题
    property PrintPageNumber: Boolean read GetPrintPageNumber write SetPrintpageNumber default false; //是否打印报表页码
    property PrintReportDate: Boolean read GetPrintReportDate write SetPrintReportDate default true; //是否打印报表日期
    property Orientation: Boolean read GetOrientation write SetOrientation default true; //页面默认为纵向
    property HeaderFields: TStrings read GetHeaderFields write SetHeaderFields; //页头上要打印的字段列表
    property DetailFields: TStrings read GetDetailFields write SetDetailFields; //明细字体列表
    property FooterFields: TStrings read GetFooterFields write SetFooterFields; //页脚上要打印的字段列表
    property FileName: string read GetFileName write SetFileName;
    property DetailSourceType: string read FDetailSourceType write FDetailSourceType; //明细数据源是'TDBGrid'还是'TDataset'
    property Grid: TCustomGrid read GetGrid write SetGrid;
    property Variants: TStrings read GetVariant write SetVariants; //报表数据字典中的变量
  end;

implementation

{ TReport }

constructor TReport.Create;
var
  band: TfrBandView;
  AppPath: string;
begin
  inherited;
  FHeaderFields := TStringList.Create; //标题字段列表
  FDetailFields := TStringList.Create; //明细字段列表
  FFooterFields := TStringList.Create; //页脚字段列表
  FGridFields := TStringList.Create; //表格字段列表
  FGridWidths := TStringList.Create; //表格字段宽度列表
  FPrintReportDate := true; //默认打印报表日期

  FOrientation := true;
  FReport := TfrReport.Create(nil); //建立报表
  //建立页面
  FReport.Pages.Clear;
  FReport.Pages.Add;
  FPage := FReport.Pages[0];
  FVariants := TfrVariables.Create;
  FVariantList := TStringList.Create;

  Band := TfrBandView.Create; //建立标题条
  Band.SetBounds(tbbLeft, tbbTop, tbbWidth, tbbHeight); //条位置
  Band.BandType := btReportTitle; //条类型
  FPage.Objects.Add(Band);

  FTitleMemo := TfrMemoView.Create; //建立标题文本
  FTitleMemo.SetBounds(tmbLeft, tmbTop, tmbWidth, tmbHeight);
  FTitleMemo.BandAlign := baCenter; //文本框居中
  FTitleMemo.Prop['Alignment'] := frtaCenter or frtaMiddle; //文本的对齐方式(垂直居中)
  FTitleMemo.Prop['Font.Style'] := 2;
  FTitleMemo.prop['Font.Name'] := mvFontName;
  FTitleMemo.Prop['Font.Size'] := 14;
  FPage.Objects.Add(FTitleMemo);

  FPrintReportDate := true;
  SetPrintReportDate(FPrintReportDate);

  frmCommon := TfrmCommon.Create(nil);
  AppPath := ExtractFilePath(Application.ExeName) + 'Reports/';
  if not DirectoryExists(AppPath) then CreateDir(AppPath);
  INIFile := TIniFile.Create(AppPath + 'Reports.ini');
end;

destructor TReport.Destroy;
begin
  FreeAndNil(INIFile);
  FreeAndNil(FHeaderFields);
  FreeAndNil(FFooterFields);
  FreeAndNil(FDetailFields);
  FreeAndNil(FGridWidths);
  FreeAndNil(FGridFields);
  FreeAndNil(frmCommon);
  FreeAndNil(FVariantList);
  FreeAndNil(FVariants);
  FreeAndNil(FReport);
  FDatasetArray := nil;
  inherited;
end;

function TReport.GetReportTitle: string;
begin
  result := FTitleMemo.Memo.Text
end;

procedure TReport.SetReportTitle(value: string);
begin
  FTitleMemo.Memo.Text := value;
  frmCommon.ReportTitle := value;
end;

function TReport.GetOrientation: Boolean;
begin
  result := FOrientation;
end;

procedure TReport.SetOrientation(value: Boolean);
begin
  FOrientation := value;
  if FOrientation then
    Fpage.ChangePaper(Fpage.pgSize, Fpage.pgWidth, Fpage.pgHeight, Fpage.pgBin, poPortrait) //页面横向
  else
    Fpage.ChangePaper(Fpage.pgSize, Fpage.pgWidth, Fpage.pgHeight, Fpage.pgBin, poLandscape); //页面纵向
  FTitleDate.SetBounds(FPage.Prop['Width'] - 70, tdbTop, tdbWidth, tdbHeight);
end;

procedure TReport.Preview;
begin
  ShowData;
  if FReport.PrepareReport then
    FReport.ShowPreparedReport;
end;

procedure TReport.Preview(FileName: string);
var
  s: string;
begin
  s := FileName;
  if s = '' then s := FFileName;
  if s = '' then exit;
  FReport.LoadFromFile(s);
  if FReport.PrepareReport then
    FReport.ShowPreparedReport;
end;

procedure TReport.Design(FileName: string);
var
  s: string;
begin
  s := FileName;
  if s = '' then s := FFileName;
  if s = '' then exit;
  FReport.LoadFromFile(s);
  if FReport.PrepareReport then
    FReport.DesignReport;
end;

procedure TReport.Design;
begin
  ShowData;
  if FReport.PrepareReport then
    FReport.DesignReport;
end;

function TReport.GetPrintReportDate: Boolean;
begin
  result := FPrintReportDate;
end;

procedure TReport.SetPrintReportDate(value: Boolean);
var
  Blank: integer;
begin
  FPrintReportDate := value;
  if FPrintReportDate then
  begin
    FTitleDate := tfrMemoView.Create;
    if FOrientation then //如果是纵向打印
      Blank := 1480
    else
      Blank := 1200;
    FTitleDate.SetBounds(FPage.pgWidth - Blank, tdbTop, tdbWidth, tdbHeight);
    FTitleDate.Memo.Add('[Date]');
    FPage.Objects.Add(FTitleDate);
  end;
end;

function TReport.GetPrintPageNumber: Boolean;
begin
  result := FPrintPageNumber;
end;

procedure TReport.SetPrintPageNumber(value: Boolean);
const
  PageNumber = '[PAGE#]/[TOTALPAGES]';
var
  mv: TfrMemoView;
  Band: TfrBandView;
  CompNo: integer;
begin
  FPrintPageNumber := value;
  Band := FindBandView(btPageFooter);
  if band = nil then //如果没有FooterBand,那么新建一个
  begin
    Band := TfrBandView.Create;
    Band.BandType := btPageFooter;
    band.SetBounds(fbbLeft, fbbTop, fbbWidth, fbbHeight);
    FPage.Objects.Add(Band);
  end;
  if FPrintPageNumber then //如果打印页码
  begin
    FReport.DoublePass := true; //报表只有显示两次才能显示页码
   
    mv := TfrMemoView.Create;
    mv.Memo.Text := PageNumber;
    mv.SetBounds(0, fbbTop, 100, mvHeight);
    mv.BandAlign := baCenter;
    mv.Prop['Alignment'] := frtaMiddle or frtaCenter;
    mv.prop['Font.Name'] := mvFontName;
    mv.Prop['Font.Size'] := mvFontSize;
    FPage.Objects.Add(mv);
  end
  else //如果不打印页码,那么删除已存在的页码文本框
  begin
    mv := FindMemoView(PageNumber, CompNo);
    if mv <> nil then
    begin
      FPage.Delete(CompNo);
      mv.Free;
    end;
  end;
end;

function TReport.FindBandView(BandType: TfrBandType): TfrBandView;
var
  v: TfrBandView;
  i: integer;
begin
  result := nil;
  for i:=0 to FPage.Objects.Count-1 do //查找是否有FooterBand
  begin
    v := FPage.objects[i];
    if (v.ClassNameIs('TfrBandView') and (TfrBandView(v).BandType = BandType)) then
    begin
      result := v;
      exit;
    end;
  end;
end;

function TReport.FindMemoView(Text: string; var CompNo: integer): TfrMemoView;
var
  i: integer;
  v: TfrMemoView;
begin
  result := nil;
  for i:=0 to FPage.Objects.Count-1 do
  begin
    v := FPage.Objects[i];
    if (v.ClassNameIs('TfrMemoView') and (TfrMemoView(v).Memo.Text = Text)) then
    begin
      CompNo := i;
      result := v;
      exit;
    end;
  end;
end;

procedure TReport.SetMemoView(MemoView: TfrMemoView; Alignment: integer; FrameTyp: word);
begin
  if MemoView = nil then exit;
  MemoView.Prop['Alignment'] := frtaMiddle or Alignment;  //文本对齐方式(默认为垂直居中)
  MemoView.prop['Font.Name'] := mvFontName; //字体名称
  MemoView.Prop['Font.Size'] := mvFontSize; //字体尺寸
  if frmCommon.MemoFrameTyp then
    MemoView.FrameTyp := FrameTyp; //边框
end;

////////////////////////////////////给打印字段列表赋值/////////////////////////////

function TReport.GetHeaderFields: TStrings;
begin
  result := FHeaderFields;
end;

procedure TReport.SetHeaderFields(const Value: TStrings);
begin
  FHeaderFields.Assign(value);
end;

function TReport.GetFooterFields: TStrings;
begin
  result := FFooterFields;
end;

procedure TReport.SetFooterFields(const Value: TStrings);
begin
  FFooterFields.Assign(value);
end;

/////////////////////////////////////////////////////////////////////////////////

function TReport.ShowFieldListForm: Boolean;
begin
  frmCommon.HeaderFields := HeaderFields;
  frmCommon.DetailFields := DetailFields;
  frmCommon.FooterFields := FooterFields;

  frmCommon.ShowModal; //显示报表设置窗体
  result := frmCommon.FormCloseMode;
  if not frmCommon.FormCloseMode then exit; //如果窗体是Cancel方式关闭的

  FTitleMemo.Memo.Text := frmCommon.ReportTitle; //返回报表标题

  //获得打印字段列表
  HeaderFields := frmCommon.HeaderFields;
  DetailFields := frmCommon.DetailFields;
  FooterFields := frmCommon.FooterFields;

  if frmCommon.SavePrintFields and (FReportName <> '') then //如果保存打印字段列表
    SaveFieldList(FReportName);
end;

procedure TReport.ShowData;

  function AnalyseText(str: string): string;
  var
    n: integer;
  begin
    result := '';
    n := AnsiPos('.', str);
    if n = 0 then exit;
    insert('"', str, n+1);
    result := str + '"';
  end;

  function GetDisLabelWidth(FieldName: string): integer; //根据字段名得到字段标签的长度
  var
    n, i, j: integer;
    s: string;
  begin
    result := 0;
    s := Fieldname;
    n := AnsiPos('.', s);
    if n > 0 then //如果字段名带有表名前缀
    begin
      Delete(s, 1, n);
    end;
    if s = '' then exit;
    for i:=0 to High(FDatasetArray) do //这个函数没考虑到前缀对应的问题,以后有空改吧
    begin
      for j:=0 to FDatasetArray[i].FieldCount-1 do
      begin
        if FDatasetArray[i].Fields[j].FieldName = s then
        begin
          result := FDatasetArray[i].Fields[j].DisplayWidth * 5; //宽度增加5倍
          exit;
        end;
      end;
    end;
  end;

var
  iCol, iLeft, i, w, h: integer;
  mv: TfrMemoView;
  Band: TfrBandView;
begin
  if FHeaderFields.Count > 0 then
  begin
    iLeft := tmbLeft; //其它字段的位置在打印日期之下
    iCol := 1; //第一行
    for i:=0 to FHeaderFields.Count-1 do
    begin
      if (i mod 3 = 0) and (i > 0) then //一行只显示三列字段
      begin
        inc(iCol, 1);
        iLeft := tmbLeft;
      end;
      mv := TfrMemoView.Create;
      SetMemoView(mv, frtaLeft, 0);
      mv.SetBounds(iLeft, tdbTop + (tdbHeight + 4) * iCol, 200, mvHeight); //4是上下Memo行之间的间隔
      mv.Memo.Text := HeaderFields.ValueFromIndex[i] + ':['
        + FDatasetArray[0].Owner.Name + '.' + AnalyseText(HeaderFields.Names[i]) + ']';
      FPage.Objects.Add(mv);
      inc(iLeft, 200);
    end;
  end
  else //否则不打印页头上的字段列表
  begin
    band := FindBandView(btReportTitle);
    Band.SetBounds(tbbLeft, tbbTop, tbbWidth, 60); //缩小条高度
  end;

  ///////////////////////////////// 明细 ///////////////////////////////////////////

  if frmCommon.UseGridRowHeight then
    h := frmCommon.GridRowHeight
  else
    h := frmCommon.MemoHeight; //得到文本框高度
  //页头
  Band := TfrBandView.Create;
  Band.SetBounds(0, PageBandTop, 0, h);
  Band.BandType := btPageHeader;
  Band.Prop['OnFirstPage'] := true;
  FPage.Objects.Add(Band);

  Band := TfrBandView.Create; //主数据条
  Band.SetBounds(0, PageBandTop + DataBandHeight * 2, 0, h);
  Band.BandType := btMasterData;
  Band.DataSet := 'frmCommon.' + frmCommon.DetailDatasetName; //明细数据条需要一个数据源
  FPage.Objects.Add(Band);

  w := 0;
  for i:=0 to FDetailFields.Count-1 do //求出明细文本框居中时的左起点
  begin
    if frmCommon.UseGridWidth then //如果使用表格的字段列表的宽度
      w := w + StrToInt(FGridWidths.ValueFromIndex[i])
    else
      w := w + GetDisLabelWidth(FDetailFields.Names[i]); //得到数据字段的显示长度
  end;
  iLeft := (FPage.Prop['Width'] - w) div 2;
  if iLeft < 32 then iLeft := 32;

  for i:=0 to FDetailFields.Count -1 do
  begin
    mv := TfrMemoView.Create; //标签
    SetMemoView(mv, frtaCenter, 15);
    if frmCommon.UseGridWidth then //如果使用表格的字段列表的宽度
      w := StrToInt(FGridWidths.ValueFromIndex[i])
    else
      w := GetDisLabelWidth(FDetailFields.Names[i]); //得到数据字段的显示长度
    mv.SetBounds(iLeft, PageBandTop, w, h);
    mv.Memo.Add(DetailFields.ValueFromIndex[i]);
    FPage.Objects.Add(mv);

    mv := TfrMemoView.Create; //记录
    mv.SetBounds(iLeft, PageBandTop + DataBandHeight * 2, w, h);
    if frmCommon.MemoFrameTyp then //文本框是否有边框
      SetMemoView(mv, frtaLeft, 15) //设置文本框属性
    else
      SetMemoView(mv, frtaLeft, 0);
    mv.Memo.Text := '[' + FDatasetArray[0].Owner.Name + '.'
      + AnalyseText(DetailFields.Names[i]) + ']';
    FPage.Objects.Add(mv);
   
    inc(iLeft, w);
  end;

  if iLeft > FPage.Prop['width'] then //如果文本框超过了页面的长度,
  begin
    SetOrientation(false); //那么页面自动转为横向打印
    FTitleDate.SetBounds(FPage.Prop['width'] - tdbWidth - 70, tdbTop, tdbWidth, tdbHeight);
  end;

  ////////////////////////////////// 页脚 ////////////////////////////////////////////////

  if FFooterFields.Count > 0 then
  begin
    Band := FindBandView(btReportSummary);
    if band = nil then //如果没有FooterBand,那么新建一个
    begin
      Band := TfrBandView.Create;
      Band.BandType := btReportSummary;
      band.SetBounds(rfbLeft, rfbTop, rfbWidth, rfbHeight);
      FPage.Objects.Add(Band);
    end;

    iLeft := tmbLeft; //其它字段的位置在打印日期之下
    iCol := 0;
    for i:=0 to FFooterFields.Count-1 do
    begin
      if (i mod 3 = 0) and (i > 0) then //一行只显示三列字段
      begin
        inc(iCol, 1);
        iLeft := tmbLeft;
      end;
      mv := TfrMemoView.Create;
      SetMemoView(mv, frtaLeft, 0);
      mv.SetBounds(iLeft, rfbTop + (tdbHeight + 4) * iCol, 200, mvHeight);
      mv.Memo.Text := FFooterFields.ValueFromIndex[i] + ':['
        + FDatasetArray[0].Owner.Name + '.' + AnalyseText(FFooterFields.Names[i]) + ']';
      FPage.Objects.Add(mv);
      inc(iLeft, 200);
    end;
  end
  else //否则不打印页脚上的字段列表
  begin

  end;
end;

function TReport.GetFileName: string;
begin
  result := FFileName;
end;

procedure TReport.SetFileName(const Value: string);
begin
  FFileName := value;
  FReport.LoadFromFile(FFileName);
end;

procedure TReport.SaveFieldList(ReportName: string);
var
  i: integer;
  s: string;
begin
  if ReportName = '' then exit;
  s := '';
  for i:=0 to HeaderFields.Count-1 do
    s := s + HeaderFields.Strings[i] + ',';
  delete(s, length(s), 1); //删除最后一个','号
  INIFile.WriteString(ReportName, 'Title', s);

  s := '';
  for i:=0 to DetailFields.Count-1 do
    s := s + DetailFields.Strings[i] + ',' ;
  delete(s, length(s), 1);
  INIFile.WriteString(ReportName, 'Detail', s);

  s := '';
  for i:=0 to FooterFields.Count-1 do
    s := s + FooterFields.Strings[i] + ',' ;
  delete(s, length(s), 1);
  INIFile.WriteString(ReportName, 'Footer', s);
end;

//从配置文件中得到要打印的字段,然后加入Listview中
procedure TReport.LoadFieldList(ReportName: string);
var
  s: string;
begin
  FReportName := ReportName;
  HeaderFields.CommaText := INIFile.ReadString(ReportName, 'Title', '');
  //注意:当调用了LoadFieldList之后,再执行SetGrid,那么DetailFields的字段列表会被FGridFields的字段列表覆盖
  DetailFields.CommaText := IniFile.ReadString(ReportName, 'Detail', '');
  FooterFields.CommaText := INIFile.ReadString(ReportName, 'Footer', '');

  if Detailfields.Count > 0 then
  begin
    s := DetailFields.Names[DetailFields.Count-1]; ;//最后一个字段的前缀就是明细数据条的数据源
    frmCommon.DetailDatasetName := Copy(s, 1, AnsiPos('.', s)-1);
  end;
end;

function TReport.GetDetailFields: TStrings;
begin
  result := FDetailFields;
end;

procedure TReport.SetDetailFields(const Value: TStrings);
begin
  FDetailFields.Assign(value);
end;

procedure TReport.SetDataset(Datasets: array of TDataset);
var
  i, j, iLen: integer;
  Dataset: TDataset;
  TvFields: TTreeView;
  MainNode: TTreeNode;
  b: Boolean;
begin
  for i:=0 to high(Datasets) do //把数据集逐一赋给FDatasetArray
  begin
    b := false;
    for j:=0 to high(FDatasetArray) do //先判断数组中是否已存在要加入的数据集
    begin
      b := false;
      if FDatasetArray[j].Name = Datasets[i].Name then
      begin
        b := true;
        break;
      end;
    end;

    if not b then
    begin
      iLen := high(FDatasetArray) + 2; //得到数组的上标(动态数组作为参数,其下标总是从0开始),长度加2才是数量
      SetLength(FDatasetArray, iLen);
      SetLength(FRDataset, iLen);

      dec(iLen);
      FDatasetArray[iLen] := Datasets[i]; //数量减去1才是维数
      FRDataset[iLen] := TfrDBDataset.Create(frmCommon);
      FRDataset[iLen].DataSet := FDatasetArray[iLen];
      frDataset[iLen].Name := FDatasetArray[iLen].Name;
    end;

  end;

  tvFields := TTreeView.Create(nil); //因为打算用一个树形结构来表示所有字段,为便于赋值,干脆把整棵树赋值给frmCommon(有时间改为Stream比较好)
  try
    tvFields.Parent := frmCommon;
    for i:=0 to high(FDatasetArray) do
    begin
      Dataset := FDatasetArray[i];
      MainNode := tvFields.Items.Add(nil, Dataset.Name);
      for j:=0 to Dataset.FieldCount-1 do
        tvFields.Items.AddChild(MainNode, Dataset.Fields[j].FieldName
          + '[' + Dataset.Fields[j].DisplayLabel + ']');
    end;

    frmCommon.tvFields.Items.Assign(tvFields.Items); //把树赋给frmCommon
  finally
    tvFields.Free;
  end;
end;

function TReport.GetGrid: TCustomGrid;
begin
  result := FGrid;
end;

procedure TReport.SetGrid(const Grid: TCustomGrid);
var
  PropInfo:PPropInfo;
  DBGrid: TDBGrid;
  i: integer;
  Dataset: TDataset;
begin
  FGrid := Grid;
  if not assigned(Grid) then exit;
  PropInfo := GetPropInfo(Grid,'DataSource'); //查找Grid是否有DataSource属性
  if PropInfo <> nil then //如果有
  begin
    DBGrid := TDBGrid.Create(nil);
    try
      //得到表格数据源的数据集的名称
      //为了不引用EhLib.pas也能得到TDBGridEh.Datasource.Dataset.Name,所以使用了RTTI
      //这样的好处不仅在TDBGridEh中体现,以后使用了别的DBGrid,只要它有DataSource,也能得到其数据集的名称
      SetObjectProp(DBGrid, 'DataSource', GetObjectProp(Grid, 'DataSource'));
      if not assigned(DBGrid.DataSource) then exit;
      Dataset := DBGrid.DataSource.DataSet;
      if Dataset = nil then exit;
      SetDataset(DataSet);

      PropInfo := GetPropInfo(Grid, 'Columns'); //查找Grid是否有Columns属性
      if PropInfo <> nil then
      begin
        SetObjectProp(DBGrid, 'Columns', GetObjectProp(Grid, 'Columns'));
        if not assigned(DBGrid.Columns) then exit;
        for i:=0 to DBGrid.Columns.Count-1 do
        begin
          if DBGrid.Columns[i].Visible then //只挑选可视的字段
          begin
            FGridFields.Add(Dataset.Name + '.' + Dataset.Fields[i].FieldName + '=' + DBGrid.Columns[i].DisplayName);
            FGridWidths.Add(Dataset.Name + '.' + Dataset.Fields[i].FieldName + '=' + IntToStr(DBGRid.Columns[i].Width));
          end;
        end;
      end;

      PropInfo := GetPropInfo(Grid, 'RowHeight'); //查找Grid是否有RowHeight属性
      if PropInfo <> nil then
      begin
        FGridRowHeight := GetPropValue(Grid, 'RowHeight', false);
        frmCommon.GridRowHeight := FGridRowHeight;
      end;

      //把DetailFields的字段列表清空,并把Grid的字段加入DetailFields中
      //这样处理有个问题:当调用了LoadFieldList后,DetailFields从FGridFields中得到的字段列表会被配置文件中的字段列表覆盖
      DetailFields.Clear;
      for i:=0 to FGridFields.Count-1 do
        DetailFields.Add(FGridFields.Strings[i]);

    finally
      FreeAndNil(DBGrid);
    end;
  end;

end;

function TReport.GetVariant: TStrings;
begin
  result := FVariantList;
end;

procedure TReport.SetVariants(const Value: TStrings);
var
  i: integer;
begin
  //注意:如果要传字符串变量,那么格式为'Name=''Value''';如果要传数值变量,那么格式为'Name=1'
  FvariantList.Assign(value);
  FReport.Dictionary.Variables[' VariantList'] := ''; //变量root
  for i:=0 to FVariantList.Count-1 do //把变量列表加入数据字典
    FReport.Dictionary.Variables[value.Names[i]] := value.ValueFromIndex[i];
end;

end.

 
原文地址:https://www.cnblogs.com/kfarvid/p/2251459.html