Adding a subgroup to another subgroup using code
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.
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.
This discussion has been closed.
Comments
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
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
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
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
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
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
lNestedSubReport.Report when loading the template...
--
Regards,
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
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
Thanks for your help, Nico.
Warm regards,
Phil
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
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
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
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;
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
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com
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
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
Nico Cizik
Digital Metaphors
http://www.digital-metaphors.com