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

Good use for subreports?

edited June 2006 in Subreports
I want to have one report, but have on it, two seperate sections.

First section would be SELECT * WHERE FIELD A IS NULL.
Second section would be SELECT * WHERE FIELD B IS NULL.

I want to show first a list of a records where field A is null, then
starting on a new page a list of records where field B is null. My first
attempt was to create two subreports on the detail band. I ended up
getting only one record per page for the first section and the second
section didn't show up at all.

Is this the approach I should be working towards?
Does the detail band have to have a pipeline attached?

(new page)
Field A nulls
1 1 1
2 2 2
3 3 3

(new page)
Field B nulls
1 1 1
2 2 2
3 3 3

Comments

  • edited June 2006
    Hi Daniel,

    Be sure each subreport is connected to separate datapipelines and that the
    main report is not connected to any data. Also, be sure that each dataset
    is actually returning data.

    If you are using Section subreports, be sure there are no bands below the
    detail band or components in the detail band.

    --
    Regards,

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

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited June 2006
    Nico Cizik (Digital Metaphors) wrote:


    I did have the main report hooked to a pipeline, once I removed that
    things are getting a lot closer. I also had the second subreport set to
    child instead of section. I also set the detail bands of the subreports
    to dynamic printheight as well.

    However the second section subreport prints the same record 20 times
    instead of printing all 20 records in it's pipeline. And it's the first
    record it keeps repeating. Any ideas?
    (Delphi 6, RB 7.04)
  • edited June 2006
    srdaniel wrote:


    Doh, when I copied the components from subreport1 to subreport2, it
    maintained the first pipeline connection. Should have seen that sooner.
  • edited June 2006
    Nico Cizik (Digital Metaphors) wrote:

    Ok, I have everything working except a a TppLabel whose OnGetText event
    I assign at runtime. In the event I lookup a code and replace the a name.

    if tblLookup.Locate( 'CodeField',
    Pipeline.DataSource.DataSet.FieldByName( 'CodeField',
    [loCaseInsensitive] ) then
    Text := tblLookup.FieldByName( 'NameField' ).asString;


    This works just fine in my other reports that I create at design time.
    With this new report I'm loading an RTM file that I previously designed
    and assigning the OnGetText event after I've loaded the file. What it
    looks like is that the dataset is not moving its record pointer, it's
    always showing the same record every time the OnGetText event fires.
  • edited June 2006
    Hi Daniel,

    If you are loading a template, the event handler will not be recognized
    unless the event was saved with that template and it is named the same.
    Otherwise you will need to assign the event in the OnLoadEnd event of the
    template.

    ----------------------------------------------
    Tech Tip: Using Template Events
    ----------------------------------------------

    The Report.Template object has several events that can be used for
    customizing what happens when a report is loaded or saved:

    - OnLoadStart
    - OnLoadEnd
    - OnNew
    - OnSaveStart
    - OnSaveEnd


    The OnLoadEnd and OnNew events are often used to perform actions related
    to report and data initialization.

    The OnSaveEnd event is often used to save additional descriptive
    ("meta") data to the database each time the report is saved.

    Example:

    The Report.Template events are public and therefore must be assigned at
    run-time.


    1. In the private section of your form declaration you can declare an
    event-handler method:

    TForm = class(TForm)
    private
    procedure myTemplateOnLoadEndEvent(Sender: TObject);

    public

    end;


    2. In the Form.OnCreate event, you can assign the event-handler to the
    event:

    procedure TForm1.FormCreate(Sender: TObject);
    begin

    ppReport1.Template.OnLoadEnd := myTemplateOnLoadEndEvent;

    end;


    3. Implement the event-handler method:

    procedure TForm1.myTemplateOnLoadEndEvent(Sender: TObject);
    begin

    {add code here to initial the report or data, etc. }
    ppReport1.PrinterSetup.MarginTop := 0.5;

    end;


    --
    Regards,

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

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited June 2006
    Nico Cizik (Digital Metaphors) wrote:


    Ok, after creating my TppReport I assign my OnLoadEnd event handler.

    rptStatementsToProcess := TppReport.Create( nil );
    rptStatementsToProcess.Template.OnLoadEnd := OnLoadEndHandler;

    From within my event handler, I call a recursive method that looks for
    my labels that need the OnGetText events hooked up.


    procedure TPendingStatementReport.OnLoadEndHandler(Sender: TObject);
    begin
    AssignObjectEventHandlers( rptStatementsToProcess );
    end;


    However the aReport.BandCount is zero, so how would I access the two
    TppLabels that I need to hookup their OnGetText events?


    procedure TPendingStatementReport.AssignObjectEventHandlers( aReport:
    TppCustomReport );
    var
    BandIdx, ObjectIdx: Integer;
    begin
    for BandIdx:=0 to aReport.BandCount-1 do
    begin
    for ObjectIdx:=0 to aReport.Bands[BandIdx].ObjectCount-1 do
    if aReport.Bands[BandIdx].Objects[ObjectIdx] is TppSubReport then
    AssignObjectEventHandlers(
    TppCustomReport(aReport.Bands[BandIdx].Objects[ObjectIdx]) )
    else if aReport.Bands[BandIdx].Objects[ObjectIdx] is TppLabel then
    AssignLabelEvent(
    TppLabel(aReport.Bands[BandIdx].Objects[ObjectIdx]) );
    end;
    end;


    procedure TPendingStatementReport.AssignLabelEvent( aLabel: TppLabel );
    begin
    if aLabel.Name = 'pplblOPDCarrierLookup' then
    aLabel.OnGetText := pplblOPDCarrierLookupGetText
    else if aLabel.Name = 'pplblOPADCarrierLookup' then
    aLabel.OnGetText := pplblOPADCarrierLookupGetText;
    end;
  • edited June 2006
    Hi Daniel,

    The band count should never be zero if the template has already been loaded.
    Below is a quick unit I created to test this and it works correctly. The
    band count is always 3.

    procedure TForm1.Button1Click(Sender: TObject);
    begin

    FReport := TppReport.Create(Self);

    FReport.Template.OnLoadEnd := ReportOnLoadEndEvent;

    FReport.Template.FileName := 'C:\Test.rtm';

    FReport.Template.LoadFromFile;

    FReport.Print;

    end;

    procedure TForm1.ReportOnLoadEndEvent(Sender: TObject);
    var
    lFont: TFont;
    begin

    lFont := TFont.Create;
    lFont.Color := clRed;
    lFont.Name := 'Arial';
    lFont.Size := 12;

    AssignFontToReport(lFont, FReport);

    end;

    procedure TForm1.AssignFontToReport(aFont: TFont; aReport: TppCustomReport);
    var
    liBand: Integer;
    liObject: Integer;
    lObject: TppComponent;

    begin

    for liBand := 0 to aReport.BandCount-1 do

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

    if lObject.HasFont then
    lObject.Font := aFont;

    end;

    end;

    --
    Regards,

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

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited June 2006
    I don't know why the BandCount was zero for me yesterday, but I got back
    to the report this afternoon and my main report had it's one detail band
    which I could then traverse looking for the controls I needed to hookup
    the OnGetText events for.

    However when I was using a TppLabel.OnGetText to lookup a name from a
    code field, my TClientDataSets didn't seem to move. The data in the
    other fields was traversing just fine but in the OnGetText event, I
    would try to do a locate on a lookup TTable based on a code field in the
    TClientDataset which was hooked up to the TdbPipeline. The value of the
    Code field in the TClientDataset however was always the same for
    everyrecord.

    So I changed the TppLabel to a TppDBText and hooked it up to the Code
    field in the TClientDataSet and turned off my OnLoadEnd of the template
    event and the Code values showed up just fine.

    Then I turned on the OnLoadEnd event again to hookup the OnGetText of
    the TppDBText controls and tried the same locate. Sure enough the value
    of the code fields was always the same. However I thought well maybe the
    Text parameter already has the correct code field and sure enough it
    did. So Instead of

    if LookupTable.Locate( 'Code', ClientDataSet.Field(Code),
    [loCaseInsensitive] ) then
    Text := LookupTable.FieldbyName( 'Name' ).asString;

    I can use

    if LookupTable.Locate( 'Code', Text, [loCaseInsensitive] ) then
    Text := LookupTable.FieldbyName( 'Name' ).asString;


    Could this be simply that I have a to old version of RB (7.04) and this
    was a bug at one point? Or I'm still missing something...(more likely!).
  • edited June 2006
    Hi Daniel,

    Instead of using the locate routine, try accessing your data directly from
    the pipeline. I assume your Label is located in the detail band of the
    report and you are accessing the dataset that is connected to the report.

    if ppReport.DataPipeline['Code'] = Text then
    ...

    --
    Regards,

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

    Best Regards,

    Nico Cizik
    Digital Metaphors
    http://www.digital-metaphors.com
  • edited June 2006
    Nico Cizik (Digital Metaphors) wrote:


    My main report doesn't have a pipeline attached. I don't quite see what
    you mean by not using the locate.

    I have a TTable connected to a paradox table, which is my lookup table.
    This is not in any way connected to the report.

    My subreports have the following connection:

    TSQLConnection->TSQLDataSet->TDataSetProvider->TClientDataSet->TDataSource->TDBPipeline->TppSubReport.

    This dataset that the subreport is connected is hooked up to a Firebird
    database. One of the fields in that Firebird database is a CODE field
    that I need to lookup the NAME for in the paradox TTable.

    The label I need to set is on the detail band of the subreport. So I
    don't quite understand how your code snippet above would work.

    I did try this however with the same results:

    if TTable.Locate( DBPipeline.DataSource.DataSet.Field(CODE) ) then
    Text := TTable.Field(NAME);
  • edited June 2006
    Hi Daniel,

    Sorry, I believe I missunderstood how your report was working. If I under
    stand correctly you are taking the CODE field from the pipeline connected to
    the subreport and using that value to locate another value in a different
    table? If a dataset is properly connected to a report/subreport it should
    traverse all the way through. If you place a DBGrid connected to the
    dataset with the CODE field in it on your form and run your report, is the
    cursor moving? You might try using the OnCalc event of a TppVariable and
    see if that makes a difference.

    --
    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.