FlexCel Report: exporting dataset of a child

Hi Adrian,
I need to produce a report that contains data from a datasets that do not share a common owner:
Example:
Form1
|
--Query1
|
--Frame1
  |
  --Query1

So my instict told me to write the template tags like this

##Query1##
##Frame1.Query1##


But i got an error (dataset not found). Is it possible to produce a report like this?

Hi,

Sadly with the v3 report engine you need everything in the same module.

I was going to explain how you could achieve this by crating a descendant from TFlexCelReport and defining a new GetDataSet method that would understand frames, but then, it was even easier if I added this support.

So we've released 3.11 today, which should understand the "##frame1.dataset##field" syntax. The named range should be "Frame1.dataset"

Just get 3.11 from our registered users page.

Regards,
   Adrian.

Thank you Adrian. I took a look at your code, tested it, and that is exactly what I needed.

Yesterday I wrote this piece of c++ code in order to implement a similar functionality:

TComponent* getSubComponent(TComponent* parent, const UnicodeString& cmpname)
{
    int p;
    if (cmpname.IsEmpty())
        return parent;
    else if ((p = PosEx(L".", cmpname)) == 0)
    {
        return parent->FindComponent(cmpname);
    }
    else
    {
        UnicodeString left = cmpname.SubString(1, p - 1);
        UnicodeString right = cmpname.SubString(p + 1, cmpname.Length());
        return getSubComponent(parent->FindComponent(left), right);
    }
}

// ---------------------------------------------------------------------------
Variant __fastcall TFormBaseF::GetCaption(System::Variant const Parameters, const int High)
{
    if (High != 0)
        return L"Invalid number of parameters";
    else
    {
        TUniControl
ctrl = dynamic_cast<TUniControl*>(FindSubComponent(Parameters[0]));
        return ctrl == NULL ? UnicodeString(L"Invalid name") : ctrl->Caption;
    }
}


// ---------------------------------------------------------------------------
Variant __fastcall TFormBaseF::GetText(System::Variant const Parameters, const int High)
{
    if (High != 0)
        return L"Invalid number of parameters";
    else
    {
        TComponent
cmp = FindSubComponent(Parameters[0]);
        TUniControl * c;
        if (cmp != NULL && (c = dynamic_cast<TUniControl*>(cmp)))
        {
            return c->Text;
        }
        return UnicodeString(L"Invalid name"); // default
    }
}

Well, that's another interesting way to do it, I hadn't thought of that. It is one thing that always surprises me on FlexCelReport: the functionality is limited, but there is a lot of ways to do workarounds with what's provided. FlexCelReport 6 (which I hopefully finally release this month) does add a lot of more interesting possibilities.


About this particular case what I originally thought was into creating a TFrameFlexCelReport as a decendant of TFlexCelReport, which overrides the "GetDataSet" method, which is the one doing the work.

In this particular case it made sense to incorporate this in the main code, but you could override GetDataSet to allow any syntax you want.

For the record, this was my "TFrameFlexCelReport" component before I decided to add the changes to the main code. It uses a class helper to access a private method, but for this version I made that method protected so it shouldn't be needed anymore.

unit UFrameFlexCelReport;

interface
uses StrUtils, Classes, DB, SysUtils, UFlexCelReport, UXlsDB, UXlsTDataSet;
type
  TFrameFlexCelReport = class(TFlexCelReport)
  private
    function GetDataSetFromComponent(const ParentComponent: TComponent;
      const DataSetName: string): IXlsDataSet;
    public
    function GetDataSet(const DataSetName: string): IXlsDataSet; override;
  end;
implementation

type
TFlexCelReportHelper = class helper for TFlexCelReport
  public
  function CalcRecordCount(const DbSet: TDataSet): integer;
end;

{ TFrameFlexCelReport }

function TFrameFlexCelReport.GetDataSet(const DataSetName: string): IXlsDataSet;
var
  FrameName: string;
  offs1, offs2: integer;
  ParentComponent: TComponent;
begin
  ParentComponent := DataModule;
  offs1 := 1;
  while True do
  begin
    offs2 := PosEx('.', DataSetName, offs1);
    if (offs2 <= 0) then break;

    FrameName := System.Copy(DataSetName, offs1, offs2 - offs1);
    offs1 := offs2 + 1;

    ParentComponent := ParentComponent.FindComponent(FrameName);
    if ParentComponent = nil then
    begin
      Result := nil;
      exit;
    end;

  end;

  Result := GetDataSetFromComponent(ParentComponent, System.Copy(DataSetName, offs1, Length(DataSetName)));
end;

function TFrameFlexCelReport.GetDataSetFromComponent(const ParentComponent: TComponent; const DataSetName: string): IXlsDataSet;
var
  Ds: TComponent;
begin
  Ds := ParentComponent.FindComponent(DataSetName);
  if (Ds = nil) then Result := nil else
    if not Supports(Ds, IXlsDataSet, Result) then
     if (Ds is TDataSet) then Result := TXlsTDataSet.Create(Ds as TDataSet, CalcRecordCount, FUseDisplayNames, FUseHiddenFields)
     else Result:=nil;
end;


{ TFlexCelReportHelper }

function TFlexCelReportHelper.CalcRecordCount(const DbSet: TDataSet): integer;
begin
  Result := Self.RecordCount(DbSet);
end;

end.