Subject Re: Report group headers
From charlie <tm@tc.com>
Date Fri, 15 Dec 2023 09:36:54 -0500
Newsgroups dbase.getting-started

Hi Mervyn, Ken...

Thanks so much for the info.  Looks doable.  Actually I am sort of amazed I figured out the sql  for the report in the first place!  Without asking questions!!!

I do have Ken's books and will refer to that also.

Merry Christmas and thanks again!!

Mervyn Bick Wrote:

> On 2023/12/14 23:47, Charlie wrote:
> > I have report where I am using group headers.  I am finding that on some pages a group header will appear at the bottom of the page, then the details on the next page.  Is there a way of preventing this from happening?  Thanks
>
> Short answer, yes.  In practice, as Ken point's out, it's complicated.
> It is, however, not particularly difficult once one gets one's head
> around it.
>
> The report designer is extremely powerful but using it isn't as
> intuitive as using the form designer.  Ken's book is invaluable when one
> needs more than just a plain vanilla report.  Believe me, it will be
> money well spent.
>
> Basically, one needs to determine how much space is needed for a
> specific group's header, its footer and how ever many records are in the
> group before one actually renders the group.
>
> As each band is prepared for rendering it updates its renderOffset
> property which gives the distance from the top of the streamframe to the
> bottom of the band.  The renderOffset value is saved when the band is
> rendered.  This allows one to use the group's FOOTERBAND_onRender()
> event handler to calculate how much of the streamframe is still
> available after a group has been rendered.  If there's enough room for
> the next group with it's header, records and footer, carry on.  If there
> isn't enough room, set the group headerband's beginNewFrame property
> true which will move to the next page.
>
> The attached example (which was done as a learning exercise back in
> 2014) is based on code from Ken's dBASE Reports book for counting the
> records in a group.  Ken's code in the book and in his examples is fully
> commented.  My code, which was created for learning rather than
> teaching, has very few comments.  I understood what needed to be done
> and I was more interested in getting it working.  You should, however,
> be able to follow the code which is actually quite straightforward.
>
> A few comments about the example.  I have no idea why I used twips as
> the metric as I normally use centimetres.  The principle is, however,
> the same no matter what metric is used.
>
> Although the detailband height is shown as 250 this was just a starting
> point before any controls were added.  The detailband's variableHeight
> property is set true and one gets its actual height for use in the
> calculation by summing the top and height properties of an appropriate
> control on the detailband.  In this case all controls are aligned so any
> control will do.
>
> The example was created while I was still learning.  Nowadays, as there
> is nothing on the group footerband, I would set its height to 0.  The
> footerband's onRender event will still be triggered even if there are no
> controls on the footerband.  It would, however, affect the spacing on
> the report.
>
> The example only works where each control on the detailband occupies one
> line.  It is also "all or nothing" when it comes to deciding whether
> there is enough room for a group or not.  With groups containing many
> records this can lead to unacceptably long blank spaces at the the
> bottom of pages.  Ken's book deals with splitting long groups over pages
> with the appropriate "continuation" text in the header on the new page
> while at the same time ensuring that one doesn't wind up with a single
> "orphan" line at the bottom of a page or at the top of the new page.
>
> If one of the controls displays data from a memofield, which may need
> several lines and the number of lines can vary from record to record,
> then simply counting records is not enough.  One also needs to calculate
> how many lines the memofield will need for each record.  This is a far
> more complicated exercise.  I do, however, have an example for this so
> just ask if you need it.
>
> Mervyn.
>
>
>
>
>
>
> clear
>
> ** END HEADER -- do not remove this line
> //
> // Generated on 2014-10-15
> //
> local r
> r = new test_countReport()
> r.render()
>
> class test_countReport of REPORT
>    with (this)
>       autoSort = false
>    endwith
>
>    this.DBASESAMPLES1 = new DATABASE(this)
>    with (this.DBASESAMPLES1)
>       left = 1080.0
>       top = 1080.0
>       width = 360.0
>       height = 360.0
>       databaseName = "DBASESAMPLES"
>       active = true
>    endwith
>
>    this.EMPLOYEES1 = new QUERY(this)
>    with (this.EMPLOYEES1)
>       left = 1080.0
>       top = 1080.0
>       width = 360.0
>       height = 360.0
>       database = form.form.dbasesamples1
>       sql = "select e.*,substring(lastname from 1 for 1) as intial from employees e order by lastname"
>       requestLive = false
>       active = true
>    endwith
>
>    this.PAGETEMPLATE1 = new PAGETEMPLATE(this)
>    with (this.PAGETEMPLATE1)
>       height = 16837.0
>       width = 11905.0
>       marginTop = 1080.0
>       marginLeft = 1080.0
>       marginBottom = 1080.0
>       marginRight = 1080.0
>       gridLineWidth = 0
>    endwith
>
>    this.PAGETEMPLATE1.STREAMFRAME1 = new STREAMFRAME(this.PAGETEMPLATE1)
>    with (this.PAGETEMPLATE1.STREAMFRAME1)
>       height = 11592.0
>       left = 360.0
>       top = 1365.0
>       width = 9360.0
>       form.STREAMFRAME1 = form.pagetemplate1.streamframe1
>    endwith
>
>    with (this.printer)
>       duplex = 1        // None
>       orientation = 1        // Portrait
>       paperSource = 268
>       paperSize = 9
>       resolution = 3        // Medium
>       color = 2        // Color
>       trueTypeFonts = 1        // Bitmap
>    endwith
>
>    this.STREAMSOURCE1 = new STREAMSOURCE(this)
>    this.STREAMSOURCE1.GROUP1 = new GROUP(this.STREAMSOURCE1)
>    with (this.STREAMSOURCE1.GROUP1)
>       groupBy = "intial"
>    endwith
>
>    with (this.STREAMSOURCE1.GROUP1.footerBand)
>       onRender = class::FOOTERBAND_ONRENDER
>       height = 250.0
>    endwith
>
>    with (this.STREAMSOURCE1.GROUP1.headerBand)
>       height = 250.0
>    endwith
>
>    this.STREAMSOURCE1.GROUP1.headerBand.TEXTINTIAL1 = new TEXT(this.STREAMSOURCE1.GROUP1.headerBand)
>    with (this.STREAMSOURCE1.GROUP1.headerBand.TEXTINTIAL1)
>       height = 300.0
>       left = 390.0
>       top = 35.0
>       width = 1530.0
>       variableHeight = true
>       prefixEnable = false
>       text = {||this.form.employees1.rowset.fields["intial"].value}
>    endwith
>
>    with (this.STREAMSOURCE1.detailBand)
>       height = 250.0
>    endwith
>
>    this.STREAMSOURCE1.detailBand.TEXTEMPLOYEEID1 = new TEXT(this.STREAMSOURCE1.detailBand)
>    with (this.STREAMSOURCE1.detailBand.TEXTEMPLOYEEID1)
>       height = 293.0
>       left = 525.0
>       top = 348.0
>       width = 1350.0
>       variableHeight = true
>       prefixEnable = false
>       alignHorizontal = 2        // Right
>       text = {||this.form.employees1.rowset.fields["employeeid"].value}
>    endwith
>
>    this.STREAMSOURCE1.detailBand.TEXTFIRSTNAME1 = new TEXT(this.STREAMSOURCE1.detailBand)
>    with (this.STREAMSOURCE1.detailBand.TEXTFIRSTNAME1)
>       height = 293.0
>       left = 2115.0
>       top = 348.0
>       width = 1530.0
>       variableHeight = true
>       prefixEnable = false
>       text = {||this.form.employees1.rowset.fields["firstname"].value}
>    endwith
>
>    this.STREAMSOURCE1.detailBand.TEXTLASTNAME1 = new TEXT(this.STREAMSOURCE1.detailBand)
>    with (this.STREAMSOURCE1.detailBand.TEXTLASTNAME1)
>       height = 293.0
>       left = 3990.0
>       top = 348.0
>       width = 1530.0
>       variableHeight = true
>       prefixEnable = false
>       text = {||this.form.employees1.rowset.fields["lastname"].value}
>    endwith
>
>    this.STREAMSOURCE1.detailBand.TEXTHIREDATE1 = new TEXT(this.STREAMSOURCE1.detailBand)
>    with (this.STREAMSOURCE1.detailBand.TEXTHIREDATE1)
>       height = 293.0
>       left = 6015.0
>       top = 333.0
>       width = 1080.0
>       variableHeight = true
>       prefixEnable = false
>       text = {||this.form.employees1.rowset.fields["hiredate"].value}
>    endwith
>
>    with (this.reportGroup.footerBand)
>       height = 250.0
>    endwith
>
>    with (this.reportGroup.headerBand)
>       height = 0.0
>    endwith
>
>    this.firstPageTemplate = this.form.pagetemplate1
>    this.form.pagetemplate1.nextPageTemplate = this.form.pagetemplate1
>    this.form.pagetemplate1.streamframe1.streamSource = this.form.streamsource1
>    this.form.streamsource1.rowset = this.form.employees1.rowset
>
>    function FOOTERBAND_onRender
>       local oStream, nCount, nSpaceRemaining, nSpaceNeeded,cInitial
>       oStream = this.parent.parent
>       oStream.bookMark = oStream.rowset.bookMark()
>       if not oStream.rowset.endofset
>          oStream.rowset.next()
>          nCount = 0
>          cInitial = substr(oStream.rowset.fields["lastname"].value,1,1)
>          do while  oStream.rowset.fields["lastname"].value = cInitial  ;
>             and not oStream.rowset.endofset
>             oStream.rowset.next()
>             nCount ++
>          enddo
>          oStream.rowset.goto(oStream.bookmark)
>       endif
>       nSpaceRemaining = this.streamframe.height - this.renderOffset
>       nSpaceNeeded =  250+250+(nCount*641)  //space for header, footer and rows
>       if nSpaceRemaining < nSpaceNeeded
>          this.parent.headerband.beginNewframe := true
>       else
>          this.parent.headerband.beginNewframe := false
>       endif
>       return
>
> endclass
>