Wendy C.


I have searched high and low for how to calculate page numbering in the header where the global values do not apply.

I have a report that generates 300+ invoices and I need the page numbers to reflect page x of xx for that invoice only. My report page breaks for each customer invoice. I have found code to get the page number in the header and I have found code to get the total pages in the body. These two solutions cannot be combined.

Has anyone found a workaround in code behind or some other method I would appreciate any guidance.

Side note: thank you all for all your postings and solutions. They have been highly valuable as I am new to SSRS.




Re: SQL Reporting Services - Page X of XX counts in header

Lisa Nicholls


Would using the number of detail items for that invoice give you that answer IOW, figure out how many are going to fit on a page Or does the row height expand differently for each invoice line (because of a long description or whatever)

If the rowheight does not expand, then I'm thinking you can use CountRows() with the scope argument limited to your invoice. What do you think

>L<







Re: SQL Reporting Services - Page X of XX counts in header

larry smithmier

Hello Wendy,

How about avoiding the problem by breaking each invoice out as a subreport That way you can utilize the built-in functionality.

As a service to others who haven't found the information you describe:

The code from Chris Hays on resetting the page number on group for getting the page number in the header:

http://blogs.msdn.com/chrishays/archive/2006/01/05/ResetPageNumberOnGroup.aspx

and using =Globals.PageNumber & " of " & Globals.TotalPages as shown on:

http://technet.microsoft.com/en-us/library/ms157274.aspx

Larry






Re: SQL Reporting Services - Page X of XX counts in header

Wendy C.

Thank you for responding to my question. The issue I am finding with row count is that the rows in a group can vary. I need a group to end (total) on one page and start the next group on the following page (keep groups together). I can also have some variation in row height for txt wrap. I have been playing with this. I was able to place a textbox in the header and point to the report item value and all looked great, page X of XX. Then I checked out the print rendering and yikes, it is all over the place. Thats where I am at right now. I will need to export to PDF and Excel too. I have another suggestion to try using a subreport, so I will look into that as well. If you can think of anything else please advise. Thanks!



Re: SQL Reporting Services - Page X of XX counts in header

Wendy C.

Hi Larry,

Thanks for providing the links for other, I will be sure to do the same in the future. I have a few questions regarding your suggestion to use a subreport. How will that reset my page counts The sub-report header is ignored <right >....and the global page numbers can only be used in the header.....could you clarify for me

Much appreciated,

Wendy





Re: SQL Reporting Services - Page X of XX counts in header

larry smithmier

Doh! I am sorry, you are absolutly correct. The subreport control renders the report without headers/footers and even strips the pagination. I have not been able to find a work around, even using custom code. It appears the main hangup is that the page is being rendered as a stream and there is no mechanism for passing information backwards in that stream to fill in placeholders. It appears to be a single pass operation.

The only way I see to get the functionality you require is to break your report into multiple smaller reports. Is there a reason why this will not work If it is the problem of printing all of the individual reports, how about automating the print process Here are a couple of articles on automating the print process:

Article showing how to print directly from SRS. Source code in C#.

http://blogs.msdn.com/bryanke/articles/71491.aspx

Codeproject article showing how to print using a temporary PDF file. Source code is in VB.

http://www.codeproject.com/sqlrs/RSPrintLandscape.asp

Another less than optimal solution would be to set your report up to display a fixed number of rows per page and calculating the number of pages per group yourself using the Count function with group level scope as discussed in the comments of this article by Brian Welcker:

If we know how many rows are on a page we can calculate the page number and force a page break! For example you group by the customer ID and add a second grouping on the row number.

Group 1 Expression

=Fields!CustomerID.Value

Group 2 Expression

=(RowNumber("table1_Group1") - 1) \ 10 + 1

On the group properties set ¡°Page break at end¡± to True

This gives us 10 records on a page. If we need to display the page number and total number of pages for this group we can use this expression in the group 2 header.

= ¡°Page ¡° + (RowNumber("table1_Group1") - 1) \ 10 + 1 & " of " & ((Count( Fields!CustomerID.Value, "table1_Group1" )-1) \ 10 + 1)

http://blogs.msdn.com/bwelcker/archive/2005/05/19/420046.aspx





Re: SQL Reporting Services - Page X of XX counts in header

Lisa Nicholls

Hi Wendy,

I understand that the number of rows in a group can vary. This is why I suggested what I suggested <s>. A CountRows() with a scope of your group should give you the answer for the group, so that you know the (varying) answer.

But with variation with row height, it probably won't work . That is why I asked about that!

Since you do have rows that vary in height, that means you have to take this route http://blogs.msdn.com/chrishays/archive/2006/01/05/ResetPageNumberOnGroup.aspx -- which I have not personally tried but it looks like it should work.

I believe there is a second possible answer that is completely different but equally wacky <g> and will take some work for me to test out (and then explain). It involves supplying some stuff externally to the report run. It does not have the shared variables issue in Chris' hack, but it is *really* wacky... Also it would only work if you have a group header that you repeat on page. Do you happen to have one of those

Chris has a full working sample attached to that post, you should go ahead and try that.

>L<






Re: SQL Reporting Services - Page X of XX counts in header

Wendy C.

Hi Lisa,

Ok, check this out...http://blogs.msdn.com/bwelcker/archive/2005/05/19/420046.aspx, the last scenario works but you then have to figure out the right row count. It does work on screen but not when it renders to print. My report has a list box containing two separate tables. The first table does not repeat as it replaces the header (it contains database field values line company info, service period, etc). My challenge is when printing a second and third page, as those pages can have more rows than the first. Chris's posting does work but only for page x, but not the total pages. I do have a group header that repeats on each page....lets go for <wacky>:-)

W.





Re: SQL Reporting Services - Page X of XX counts in header

Lisa Nicholls

Wendy -- I would have posted it if I could do it easily. Unfortunately it's not something I have a postable example of... can we take this one off line, please (you can e-mail me as lisa at spacefold dot com).

If you can wait, I will try to write something over the weekend, but again there are external pieces to this solution and I'm not sure you will be comfortable with that. So I'd rather have a chat about it...

>L<






Re: SQL Reporting Services - Page X of XX counts in header

Lisa Nicholls

one more thing -- this will only work on an RDL, not on an RDLC...

>L<






Re: SQL Reporting Services - Page X of XX counts in header

Lisa Nicholls

Hi Wendy et al,

It's the weekend now and I've had a chance to work on this a bit...

Here's the deal: Chris' hack gave you the group page numbers, Wendy, but not the group totals, right Well, by adapting his function, and using it to preprocess the report in a silent run, you can get total number of pages for the group.

This is why I said it would take some surrounding code (you couldn't do this straight from reportserver, you'd want an app that could run the report once without showing it and then again for the "real" run.

In this sample (which is all I've had time to do) I'm just shooting a log of the group page numbers to a text file. Generally I'd want to send the results to an xml file, and the name of the file would be a hidden parameter to the report.

On my first run, I would erase the file if it existed (we'd generate a temp file name anyway, but it doesn't hurt to check) and then give this filename to the report as a parameter. I'd write to the file using a really simple xml syntax. The function you see below is my adaptation of Chris' for this purpose.

On the second run, I would still want that parameter so I could load up this file as a document and grab the values, matching them to the group values, with some equally simple XPATH. The groupNo var you see would make the xpath bizarrely simple, actually, because you could just use it for the index into the nodes collection -- you wouldn't really even need to store the group name value for name identification purposes even though I'm doing it in my text file here.

I would keep the two functions separate. They would "know" which run we were on (whether preprocess or real) with a second, boolean, parameter.

Both functions could send values back on both runs, the adapted version of function #1 sending back group page number as you see here and function #2 sending the group page total.. The page values coming back from function #2 would be spurious on the preprocess run, but that wouldn't affect anything.

The reason I'm not writing the second function here is that I'm not sure that my method of writing to an xml temp file is going to be the way that other people want to do this -- maybe there's another approach writing to a table or something, it's just the way I tend to do things. Also some other method would work better for a server report. IAC you can write code that runs during a report, therefore something like this has to be possible. I think the main idea (preprocess to get the values, then do your real run) should be adaptable.

(FWIW I would get around the shared issue that Chris talks about, at least for these values, if I were writing to a table, by adding a key representing this run to "my" rows in the table. But where possible a private little XML file is sure handy <s>. )

I *don't* think it would be a good idea to do a preprocess using a custom renderer unless you could guarantee that pagination would be the same. So, in the case of printing, to make sure that pagination was exactly like what you would get when you printed, I'd have a second printer setup with the same attributes that went out to an empty port or printed to file or something for run #1.

(It would be nice if there was a hook to run the report in a no-output mode like this, for just this purpose, with correct pagination, in the default renderers. As far as I know RS doesn't provide this feature, so we're just inventing it until they get around to it <s>.)

So, okay, to review, Chris' instructions and function look like this:

Code Snippet

(http://blogs.msdn.com/chrishays/archive/2006/01/05/ResetPageNumberOnGroup.aspx)

Step 1: Make sure there's a textbox in the report which contains the group expression

Step 2: Add shared variables to track the current group and page offset
Shared offset as Integer
Shared currentgroup as Object

Step 3: Add a custom function to set the shared variables and retrieve the group page number
Public Function GetGroupPageNumber(group as Object, pagenumber as Integer) as Object
If Not (group = currentgroup)
offset = pagenumber - 1
currentgroup = group
End If
Return pagenumber - offset
End Function

Step 4: Use the function in the page header or footer
=Code.GetGroupPageNumber(ReportItems!Category.Value,Globals!PageNumber)

In my Step 4, the function you put in the page header or footer looks like this (new last arg):

Code Snippet

Code.GetGroupPageNumber(ReportItems!txtGroupHeaderItem.Value ,Globals!PageNumber,Globals!TotalPages )

I'll highlight my code additions below:

Code Snippet

Shared offset as Integer
Shared currentgroup as Object
Shared groupNo As Integer = 0
Dim sw As System.IO.StreamWriter

Public Function GetGroupPageNumber(group as Object, _

pagenumber as Integer, total As Integer) as Object
If Not (group = currentgroup)
If groupNo = 0 Then
sw = New System.IO.StreamWriter("c:\temp\TestFile.txt")
Else
sw.WriteLine("Group " & currentgroup & " last page: " & CSTR(pagenumber-1))
End If
groupNo += 1
offset = pagenumber - 1
currentgroup = group
End If
If (pagenumber = total) Then

sw.WriteLine("Group " & currentgroup & " last page: " & CSTR(total))
sw.Close()
sw = Nothing
End If
Return pagenumber - offset
End Function

You can see there isn't anything tricky here; the sample output gives you a set of "last pages" which you can then use, with subtraction, to figure out how many pages were in each group. For example, my output looks like this:

Group A last page: 1
Group B last page: 2
Group C last page: 3
Group D last page: 4
Group E last page: 30
Group F last page: 44
Group G last page: 45
Group H last page: 50
Group I last page: 51
Group J last page: 52
Group L last page: 55
Group M last page: 56
Group N last page: 61
Group O last page: 66

... so you can see that A, B, C, and D had 1 page each, E had 26 pages, etc.

>L<






Re: SQL Reporting Services - Page X of XX counts in header

techy_007

I was trying this example but it seems this line is not working

sw = New System.IO.StreamWriter("c:\temp\TestFile.txt")

I am cluless how to resolve this. Please let me know or send working code.

Thanks!





Re: SQL Reporting Services - Page X of XX counts in header

Lisa Nicholls

I *did* send working code. <s>

What error are you getting

Do you have a c:\temp directory and does your identity (for example, if you are doing this on a web server) have full rights to that directory

I was very specific in my description of this method about this being a sketch.

As I said in the message, the c:\temp\testfile.txt file will be replaced by a *generated file name* which I expected one would put in the appropriate directory (generally I ask Windows for a tempory file name, so I don't have to worry about where it's going, but you could still have problems there if your identity isn't set up correctly).

I also said that this file will also be replaced by a *file in a different format*, probably XML.

If you go ahead with this method, so you need to read the whole message and think, from the beginning, about what we are trying to accomplish, not just run the code <s>. OK

>L<






Re: SQL Reporting Services - Page X of XX counts in header

techy_007

Hi Lisa,

Let me tell you what I am trying to accomplish,

Let say there are total 15 Pages in the report and there are 3 groups with page break then I need something like

For Group 1 paging would be -> Page 1 of 3, Page 2 of 3.....etc

For Group 1 paging would be -> Page 1 of 5, Page 2 of 5, Page 3 of 5...etc

For Group 1 paging would be -> Page 1 of 7

Currently I am getting like

Group 1 - Page1 of 15, Page 2 of 15...ect

Group 2 - Page 1 of 15, Page 2 of 15..etc

Group 3 - Page 1 of 15, Page 2 of 15..etc

I like the idea that you suggested and started implementing. I have "C:\temp\TestFile.txt" directory in place with proper permissions etc.

Following code generate "#Error" when report is rendered. I am using "LOCAL REPORT" in a Win forms application and added ".rdlc" file as an item in the solution.

__________

Dim sw As System.IO.StreamWriter
sw = New System.IO.StreamWriter("C:\temp\TestFile.txt")
sw.WriteLine("Group")
sw.Close()

__________

I tested this code from a windows application and that works fine. Is there any other way to store the last page number for every group

Thanks a lot for your help.





Re: SQL Reporting Services - Page X of XX counts in header

Lisa Nicholls

I absolutely understand what you are trying to accomplish.

I am not sure that you understand that the method I'm showing you is a sketch. You can substitute other code for writing to the test file on a web server, or you can make sure to generate a file name, which you would pass to the report as a parameter as I have already described in a suitable place.

But the point is that the method is trying to show you the best event for getting the data that you want. The test output in the TXT file is not supposed to be the result that you use in production. It just shows you the stored results, not how to store them.

As I have already said, if I was working this out I would probably want to create an XML file with the results, but somebody else might prefer to creae a record in a database, or multiple records with a run ID of some sort. This would, as I have also already said, get around the "shared" variable problem in the original method.

If I were writing to a record in a database,I would still pass in a key value for the row as a parameter to a report, just as if I were writing to a file I would pass in the generated file name in the correct position for me to write to. I would probably store all the data as XML (or NTEXT), so I would be building up the data throughout the report and then do one INSERT or UPDATE with the full value. This would happen on the first run. On the second run I would still have that parameter and I would *retrieve* that data that I had stored on the first run.

If I were writing to a database I would still need permissions, just not file permissions. So you still have to decide how best to do it in your environment, with the tools with which you are most comfortable. I am not making those decisions for you.

You need to understand what I am doing for you and what I am only suggesting in this example. It is not meant to be a "turnkey" solution. It is meant to show you how to do it but you still have to write some code the way you are best fit to write it.

>L<