Home Subreports
New Blog Posts: Merging Reports - Part 1 and Part 2

Adding a subgroup to another subgroup using code

edited March 2009 in Subreports
Using ReportBuilder 11, I am constructing a report completely in code. Using examples found in the reportbuilder wiki, I can successfully create the report and subreports. Howeverk, in some cases, I need to create a subreport nested inside a subreport. I don't know how to assign my new, child subreport to its parent subreport.

Here is the code I use to create a subreport within the DetailBand.
lSubReport.Band := rbDesignerReport.DetailBand;

lSubReport.CreateReport(rbDesignerReport);
lSubreport.Name := 'sr' + ReportTemplateName;
lSubreport.UserName := 'sr' + ReportTemplateName;
lSubreport.spTop := 19 * (ThisCounter - 1);

So now I want to place another subreport within this one, and am not able to see how to do it.

Thanks.

Comments

  • edited March 2009
    Correction, the subject line should read "Adding a subreport to another subreport. . . ."
  • edited March 2009
    Hi Phil,

    There are two parts to a subreport, the TppSubreport which is what you place
    on the main report and the TppChildReport which is the actual design of the
    subreport. If you would like to add a subreport to a current subreport, you
    will need to use the TppChildReport object as the parent. This can be
    accessed using the TppSubreport.Report property. Creating a separate
    variable for the child report can make things easier...

    var
    lSubreport: TppSubreport;
    lChildReport: TppChildReport;
    lNestedSubreport: TppSubreport;
    ...
    begin

    ...
    lChildReport := TppChildReport(lSubreport.Report);

    lNestedSubreport.Band := lChildReport.DetailBand;
    lNestedSubreport.CreateReport(lNestedReport.Report);

    --
    Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited March 2009
    Hi Nico,

    This is very helpful. I did not mention in the first post that I want to refer to the parent subreport by name (a variable). The report I am building can contain many subreports and nested subreports.
    Each subreport is assigned a name and I indicate the parent subreport by the variable ParentTemplateName.

    So, in the following line, I think I need to substitute Subreport.Report with the variable ParentTemplateName but I am not sure what this should look like.
    lChildReport := TppChildReport(lSubreport.Report);

    Thanks again - and I must say that I am extremely pleased with the abilities of ReportBuilder - it is pretty amazing what can be done!

    Phil
  • edited March 2009
    Hi Phil,

    To gain access to a report object that has already been created, you will
    either need to use a report object loop or keep track of a reference to
    those objects after they are created. For instance, you could create a
    helper function that takes a subreport name and returns the created
    subreport reference. First take a look at the following article on how to
    create a report object loop.

    http://www.digital-metaphors.com/rbWiki/Delphi_Code/Layouts/Report_Object_Loop

    Your function would be similar except instead of the font logic it would
    have something like the following (using recursion to iterate through every
    nested subreport...

    function RetrieveSubreport(aSubreportName: String; aReport:
    TppCustomReport): TppSubreport;
    var
    ...
    begin

    Result := nil;
    ...

    if (lObject is TppSubreport) then
    if (TppSubreport(lObject).Name = aSubreportName) then
    Result := TppSubreport(lObject)
    else
    Result := RetrieveSubreport(aSubreportName,
    TppSubreport(lObject).Report);
    ...
    end;

    And you would change the line below to: lChildReport :=
    RetrieveSubreport(ParentTemplateName, MainReport).Report;

    --
    Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited March 2009
    Hello Nico,

    I have tried to implement your instructions, but am failing somewhere.

    Following is the code snippet that is intended to add a nested subreport. It is actually not inserting a nested subreport. Rather it is loading the nested Report Template into the "parent" subroutine and thus I am losing the parent design content.

    To be specific, the parent subreport is named srProgramObjectives.I want to create another subreport called spProgramObjectiveActivities in the subreport srProgramObjectives.

    My code (not shown here) correctly builds the subreport srProgramObjectives. But when include the process to add the nested subreport srProgramObjectiveActivities is not created and the template for srProgramObjectiveActivitiesis placed into srProgramObjectives.

    In the code snippet below,
    ParentTemplateName = 'ProgramObjectives'
    ReportTemplateName = 'ProgramObjectiveActivities"


    code snippet. . . .

    var
    lSubReport: TppSubReport;
    lReport: TppChildReport;
    lChildReport: TppChildReport;
    lNestedSubreport: TppSubreport;

    ReportTemplateName: string;
    ParentTemplateName: string;
    ReportParams: TStrings;
    I: integer;
    begin
    . . .
    lChildReport := TppChildReport(RetrieveSubreport('sr' + ParentTemplateName, rbDesignerReport).Report);

    lNestedSubReport := TppSubReport.Create(Self);
    lNestedSubReport.Band := lChildReport.DetailBand;

    lNestedSubReport.CreateReport(rbDesignerReport);
    lNestedSubReport.Name := 'sr' + ReportTemplateName;
    lNestedSubReport.UserName := 'sr' + ReportTemplateName;
    lNestedSubReport.spTop := 19 * (ThisCounter - 1);


    lChildReport.Template.DatabaseSettings.NameField := 'ReportTemplateName';
    lChildReport.Template.DatabaseSettings.TemplateField := 'ReportTemplate';
    lChildReport.Template.DatabaseSettings.DataPipeline := plreportTemplate;
    lChildReport.Template.DatabaseSettings.Name := ReportTemplateName;
    lChildReport.template.LoadFromDatabase;
    end;

    What am I missing?

    Thanks,
    Phil Horst
  • edited March 2009
    lChildReport references to the parent report. You want to use
    lNestedSubReport.Report when loading the template...



    --
    Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited March 2009
    Hi Nico,

    That solved this particular problem. Thanks!

    Now I have one remaining problem to solve (I think).

    If I have more than one child subreport in a particular subreport, I need to assign the ShiftRelativeTo property for child subreports to allow stretching of the child subreports.

    The order the child subreports are added to the parent determines the relative postion.

    Can I use the looping technique to identify the subreports in a particular subreport? Any other techniques?

    Thanks again for your help. When this problem is solved, ReportBuilder will make my program look amazing!

    Phil Horst
  • edited March 2009
    Found a solution.

    Thanks for your help, Nico.

    Warm regards,
    Phil
  • edited March 2009
    I have a situation where I need to have a subreport shift relative to another stretchable component, it might be a region or a db memo field.

    I know the name of the component - I am requiring this as part of the definition of the report.

    In order to assign the sbureport ShiftRelative property, I need to find the component in the report based on its user name.

    Earlier in this thread, there is an example of how to find a subreport based on its name using the looping technique. I tried a similar technique
    looking for TppStrechable components, but it does not work. Any help with this? Or is there a better way to do this?

    My procedure to (try to) locate stretchable compoenents is as follows:

    function TdmReportBuilder.RetrieveStretchableComponents(aParentName: String;
    aReport: TppCustomReport): TppStretchable;
    var
    liBand: Integer;
    liObject: Integer;
    lObject: TppComponent;

    begin
    Result := nil;

    for liBand := 0 to aReport.BandCount-1 do
    begin
    if not (result = nil) then
    exit;

    for liObject := 0 to aReport.Bands[liBand].ObjectCount-1 do
    begin
    lObject := aReport.Bands[liBand].Objects[liObject];

    if (lObject is TppStretchable) then
    begin
    if (TppStretchable(lObject).UserName = aParentName) then
    begin
    Result := TppStretchable(lObject);
    exit;
    end
    else
    Result := RetrieveStretchableComponents(aParentName,
    TppStretchable(lObject).Report);
    end;
    end;
    end;
    end;


    Thanks, as always, for your help!
    Phil
  • edited March 2009
    Hi Phil,

    Are you certain the UserName is identical to the aParentName parameter? Try
    tracing into the if condition and place a watch on UserName and aParentName
    to see what the problem might be.

    --
    Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited March 2009
    Hi Nico,

    I am certain the UserName should find a match.

    The procedure RetrieveStretchableComponents fails in an infinite loop. In tracing the code, I see that
    each recursion is always testing the same object in the line
    . . .
    if (TppStretchable(lObject).UserName = aParentName) then
    . . .

    To be more specific, the first three subreports in my example are called
    srPageHeaderCountryName
    srPageHeaderReportTemplateTitle shiftrelative to srPageHeaderCountryName
    srPageHeaderYearandRep shiftrelative to srPageHeaderReportTemplateTitle

    The function RetrieveStretchableComponents works for the subreport srPageHeaderReportTemplate title in that
    it finds srPageHeaderCountryName.

    On the third subreport, the function always finds srPageHeaderCountryName. Since this not a match,
    it recurses. Each recursion finds, again, srPageHeaderCountryName. So it loops until running out of stack space.


    Interestingly, this procedure, RetrieveSubreport, based on your earlier correspondance works using thee same data.
    The only change in the program is to change function calls from RetrieveSubreport to RetrieveStretchableComponents.
    Here is the function RetrieveSubreport for comparison.

    function TdmReportBuilder.RetrieveSubreport(aSubreportName: String;
    aReport: TppCustomReport): TppSubreport;
    var
    liBand: Integer;
    liObject: Integer;
    lObject: TppComponent;

    begin
    Result := nil;

    for liBand := 0 to aReport.BandCount-1 do
    begin
    if not (result = nil) then
    exit;

    for liObject := 0 to aReport.Bands[liBand].ObjectCount-1 do
    begin
    lObject := aReport.Bands[liBand].Objects[liObject];

    if (lObject is TppSubreport) then
    begin
    if (TppSubreport(lObject).UserName = aSubreportName) then
    begin
    Result := TppSubreport(lObject);
    exit;
    end
    else
    Result := RetrieveSubreport(aSubreportName,
    TppSubreport(lObject).Report);
    end;
    end;
    end;
    end;
  • edited March 2009
    Hi Phil,

    Thanks, I see the issue now. In your RetrieveStretchableComponents routine,
    you are assuming that a TppStretchable object is a subreport when in
    actuality it can be a number of components.

    If for instance you come across a TppMemo which is a stretchable component
    and the UserName does not equal the aParentName, your function will try to
    make a recursive call using the TppMemo.Report property which in this case
    happens to be the main report (hence the reason you are getting an infinite
    loop). You will need to be sure you only make the recursive call to
    RetrieveStretchableComponents when a Subreport is found.

    if (lObject is TppStretchable) then
    begin
    if (TppStretchable(lObject).UserName = aParentName) then
    begin
    Result := TppStretchable(lObject);
    exit;
    end
    else if (lObject is TppSubreport) then
    Result := RetrieveStretchableComponents(aParentName,
    TppStretchable(lObject).Report);
    end;

    --
    Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited March 2009
    Hi Nico,

    Thanks for the pointer. I had to make one more adjustment and now it works!
    I had to change the casting to TppSubreport at line indicated rather than using TppStretchable.
    With this change it works. I don't understand enough to know why this is needed.

    I now can build nested subreports and make them shift relative to any stretchable component in the parent component. Pretty nice!

    else if (lObject is TppSubreport) then
    Result := RetrieveStretchableComponents(aParentName,
    --> TppSubreport(lObject).Report);

    Thanks again,
    Phil
  • edited March 2009
    Hi Phil,

    Yes you are correct, sorry I missed that.

    The reason you need to typecast the lObject variable as a TppSubreport is
    that the .Report property returns a different value than its ancestor class
    TppStretchable. The property "getter" GetReport is overridden in the
    ancestor class (TppSubreport) to return the child report rather than the
    main report.

    --
    Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
This discussion has been closed.