Showing posts with label ReportViewer. Show all posts
Showing posts with label ReportViewer. Show all posts
Thursday, 18 October 2012
Microsoft ReportViewer - Change Subreport at runtime (rdlc)
Its possible to change a subreport at runtime using the below code.
Both reports need to be loaded from a Stream in order for this to work, but is easily achievable by adding a method to the reporting library to handle this.
The reporting Assembly contains a 'Common' Class with the following static methods:
And to call this code from my web application the following code can be used
Labels:
ASP,
ReportViewer
Friday, 27 January 2012
How to to save an rdlc to PDF programatically
If you need to save a report viewer report to PDF via cocdeit can be achieved with the below code.
Labels:
c#,
ReportViewer
Friday, 14 October 2011
Referencing Control libraries in Web.Config - not each page
I have had an issue where there are different versions of assemblies loaded for the Microsoft report viewer. This was caused because the web config was referencing one version of the dll's and the pages were referencing another.
To fix this issue I removed all references to the report viewer assembly that were contained in aspx pages, and added an entry to the reportviewer dll to the web config so it can be accessed via the specified tag.
eg:
extracts from the web.config, here you can see the referenced ReportViewer assemblies, and where the controls are.
You can then use the report viewer controls in an asp page with the following:
To fix this issue I removed all references to the report viewer assembly that were contained in aspx pages, and added an entry to the reportviewer dll to the web config so it can be accessed via the specified tag.
eg:
extracts from the web.config, here you can see the referenced ReportViewer assemblies, and where the controls are.
You can then use the report viewer controls in an asp page with the following:
Labels:
ASP,
ReportViewer
Tuesday, 6 September 2011
Show label on ReportViewer report based on whether its the last page
If you need to show a field on a Reportviewer report based on whether its the last page of the report or no (eg Totals etc) then it can be achieved with the following code:
To Show on Last page should be:
To Show on All pages but the last should be:
To Show on Last page should be:
To Show on All pages but the last should be:
Labels:
ReportViewer
Show Page X of Y in ReportViewer Footer
if you need to show the current page and total page count on a ReportViewer report it can be achieved with the following expression:
Labels:
ReportViewer
Thursday, 7 April 2011
ReportViewer - object datasource, nested objects return #Error
If you are using Visual studio 2010 and Report viewer you may have experienced the problem with using Nested Properties on the report with the fields returning #Error.
This has been fixed in Service Pack 1 for Visual studio 2010 although you must ensure all classes referenced in the report have the [Serializable] attribute for EVERY property that is a user type, they must also be a public class, and also a public constructor with no parameters. Without these you will still get the error. When a class is serialized it cannot be unserialized without the Default constructor as the class is instanitated with that constructor, then the properties set once the class is created. It also seems that if the entire class cannot be serialized the report will not show it.
To use a nested property on the report the following expression should be entered:
=Fields!Supplier.Value.SupplierLocation.Address1
This expression adds a field to the report that is three properties deep from the object the report is bound to.
The report datasource binds to an Order class, the order class contains a Supplier property, and the Supplier class contains a Supplier Location Property.
The Order, Supplier and Supplier Location class must use the [Serializable] attribute, and also any other property of any of the classes referenced. eg, Just make every class in your project Serializable and you should be good to go. ( technically you dont need every class but this was easier than checking every property of each nested object to make sure its serializable.
eg:
UPDATE: 16/06/2011
I tried to do another report with a different object and the #Error problem returned. However, After going though every single property that is a class in my datasource, and adding the serializable attribute to the report started working again. So, Every property of the object needs to be serializable, otherwise it seems the whole object cannot be serialized.
Checklist:
UPDATE: 22/06/2011
Sample project can now be downloaded from
Sample Web Project
This has been fixed in Service Pack 1 for Visual studio 2010 although you must ensure all classes referenced in the report have the [Serializable] attribute for EVERY property that is a user type, they must also be a public class, and also a public constructor with no parameters. Without these you will still get the error. When a class is serialized it cannot be unserialized without the Default constructor as the class is instanitated with that constructor, then the properties set once the class is created. It also seems that if the entire class cannot be serialized the report will not show it.
To use a nested property on the report the following expression should be entered:
=Fields!Supplier.Value.SupplierLocation.Address1
This expression adds a field to the report that is three properties deep from the object the report is bound to.
The report datasource binds to an Order class, the order class contains a Supplier property, and the Supplier class contains a Supplier Location Property.
The Order, Supplier and Supplier Location class must use the [Serializable] attribute, and also any other property of any of the classes referenced. eg, Just make every class in your project Serializable and you should be good to go. ( technically you dont need every class but this was easier than checking every property of each nested object to make sure its serializable.
eg:
UPDATE: 16/06/2011
I tried to do another report with a different object and the #Error problem returned. However, After going though every single property that is a class in my datasource, and adding the serializable attribute to the report started working again. So, Every property of the object needs to be serializable, otherwise it seems the whole object cannot be serialized.
Checklist:
- ALL classes are serializable (every user type in the class must be serializable, and any user type in a property of a usertype must be serialzable)
- ALL classes have a public parameterless constructor
- ALL classes used in the report must have the public modifier
- If any property of the datasource, or any property of a property cannot be serialized then your will get the #Error. Just make sure everything is serializable
- Make sure there will be no infinite recursion issues - eg, class A has a property of class B, and class B has a property of class A. Use XMLIgnore / ScriptIgnore attributes
UPDATE: 22/06/2011
Sample project can now be downloaded from
Sample Web Project
Labels:
ReportViewer,
Visual Studio 2010
ReportViewer - Prints Extra blank pages or splits accross multiple pages
If you are using report viewer and find that when you export to PDF or print the report blank pages are coming out, or some fields are on the page on there own. This is to do with page size and margins.
When you are editing the rdlc file in design mode, firstly click on an empty part of the BODY area of your design. Hit F4 to see the properties tab. Here, you will see a "Size" property. This can be expanded for the width and height. The width you see here represents the width that the body of your report requires as printable area. Even if you have white space all over, the page knows that it needs to keep it as a printable area. It reserves the space, in some sense. As for the height, the system generally knows that it can grow or shrink as necessary, unless you have specified otherwise within your controls therein. So the width is what will, usually, play the most important role.
Next, click on an empty area of the report (outside the header, body, and footer; basically the gray area around the design), then hit F4 to view the properties panel. Under the "Layout" category of the properties, you will see 3 different options: InteractiveSize, Margins, PageSize. Each of those Size attributes can be expanded to show the Width and Height. The Margins attribute can be expanded for the left/right/top/bottom.
Basically, the pdf export works out of the PageSize (though I generally try to keep Interactive and Page size equal). When the pdf file is rendered via the ReportViewer's built-in export function, the width and height of each "page" within the pdf will be determined by the width and height in the report's PageSize attribute (you could override this if you used your own custom code for the pdf rendering). As for the margins, they specify how much space MUST be left blank and unprintable between the printable area reserved for your report and the edge of the page.
In other words: Your report's Body's Width, Plus the Report's Left Margin, Plus the Report's Right Margin, MUST be smaller than or equal to the Report's PageSize's Width!
So...if your margins are too wide, or if your report's body is too wide, or if the PageSize's width is too narrow, the rendered result is forced to be broken down to multiple pages in order to fit!
For example: If my report's body has width 7.75", my Left margin is 0.5", my right margin is 0.5", and the width specified in the PageSize is 8.5", my report will always use 2 pages for each 1 page of data. The 7.75" width of the body, plus 0.5"+0.5" for the margins add up to 8.75", which is larger than the 8.5" available in my page. So the first 7.5" (or so) of each page of my report's body will be shown in the first page, and the rest will be split down to the next page. This will not be done inside the report viewer, as it allows for the report to grow beyond the page size by just adding a scrollbar, but it will be annoyingly noticeable in the pdf export. In order to make my example report fit in 1 page, I can either try and reduce the body of my report to 7.5" or less, or I can reduce the left and right margins by a total of 0.25" or more (for example, set them to 0.3" for a total reduction of 0.4"), or I can increase the PageSize to something larger than 8.75". Note: Acrobat Reader is pretty smart and knows about various paper sizes. Therefore, while arbitrary PageSizes will work, it is typically best to use real page sizes. As such, in my last example I would rather set the PageSize to have Width = 11" and Height = 8.5", which is a real letter-size in landscape! Adobe will typically understand this and print properly. Also Note: Some printers, especially older ones, have trouble printing with less than 0.3" margins. If you want to be nice to your users, you should best keep the margins large enough for those older printers ;)
When you are editing the rdlc file in design mode, firstly click on an empty part of the BODY area of your design. Hit F4 to see the properties tab. Here, you will see a "Size" property. This can be expanded for the width and height. The width you see here represents the width that the body of your report requires as printable area. Even if you have white space all over, the page knows that it needs to keep it as a printable area. It reserves the space, in some sense. As for the height, the system generally knows that it can grow or shrink as necessary, unless you have specified otherwise within your controls therein. So the width is what will, usually, play the most important role.
Next, click on an empty area of the report (outside the header, body, and footer; basically the gray area around the design), then hit F4 to view the properties panel. Under the "Layout" category of the properties, you will see 3 different options: InteractiveSize, Margins, PageSize. Each of those Size attributes can be expanded to show the Width and Height. The Margins attribute can be expanded for the left/right/top/bottom.
Basically, the pdf export works out of the PageSize (though I generally try to keep Interactive and Page size equal). When the pdf file is rendered via the ReportViewer's built-in export function, the width and height of each "page" within the pdf will be determined by the width and height in the report's PageSize attribute (you could override this if you used your own custom code for the pdf rendering). As for the margins, they specify how much space MUST be left blank and unprintable between the printable area reserved for your report and the edge of the page.
In other words: Your report's Body's Width, Plus the Report's Left Margin, Plus the Report's Right Margin, MUST be smaller than or equal to the Report's PageSize's Width!
So...if your margins are too wide, or if your report's body is too wide, or if the PageSize's width is too narrow, the rendered result is forced to be broken down to multiple pages in order to fit!
For example: If my report's body has width 7.75", my Left margin is 0.5", my right margin is 0.5", and the width specified in the PageSize is 8.5", my report will always use 2 pages for each 1 page of data. The 7.75" width of the body, plus 0.5"+0.5" for the margins add up to 8.75", which is larger than the 8.5" available in my page. So the first 7.5" (or so) of each page of my report's body will be shown in the first page, and the rest will be split down to the next page. This will not be done inside the report viewer, as it allows for the report to grow beyond the page size by just adding a scrollbar, but it will be annoyingly noticeable in the pdf export. In order to make my example report fit in 1 page, I can either try and reduce the body of my report to 7.5" or less, or I can reduce the left and right margins by a total of 0.25" or more (for example, set them to 0.3" for a total reduction of 0.4"), or I can increase the PageSize to something larger than 8.75". Note: Acrobat Reader is pretty smart and knows about various paper sizes. Therefore, while arbitrary PageSizes will work, it is typically best to use real page sizes. As such, in my last example I would rather set the PageSize to have Width = 11" and Height = 8.5", which is a real letter-size in landscape! Adobe will typically understand this and print properly. Also Note: Some printers, especially older ones, have trouble printing with less than 0.3" margins. If you want to be nice to your users, you should best keep the margins large enough for those older printers ;)
Labels:
ReportViewer
Saturday, 19 February 2011
ReportViewer - Sub Report with integrated security problem
I have recently had a problem with using Sub Reports in Report Viewer when the SQL server is a seperate box from the web server. The initial data is loaded correctly but when the subreport processing event is fired the second SQL command comes back with a failed login error as it was trying to connect with the local system account of the web server. The connection strings to the database are all using Integrated Security so the impersonation context needs to be there to access the database as the correct user.
After much investigation is looks as though this is caused by a phenomenon called a 'double hop'. The request is sent from the web server, to the SQL server and identity information is passed across to the SQL server. When the sub report is processing the request is again made to the SQL server but at this point the indentity information is lost as it only lasts 2 'hops' This is aparently by design for NTLM authentiation. I have read this can be fixed by using Kerberos authentication but I know nothing about this.
Two solutions that I have come up with seem to work.
1) Change all connection strings to the database to explicity specify the UserID and Password for the SQL connection. This seems to work ok but unless you want to mess about encrypting web configs and connect strings then this isnt really an option.
2) Change the user account the web applications app pool is running under. I changed this account to use the same domain user account that the web application was impersonating. It seems the sub report request is using the identity of the application pool to connect. Changing this then uses the correct user account for the integrated security in the connection string. One thing to note with this method is that I had to change the Anonymous access user in IIS to also use this domain account as a login box was being presented when browsing to the site.
Here is the code for the aspx page that is loading the report. The problem manifests itself in the LocalReport_SubreportProcessing event.
After much investigation is looks as though this is caused by a phenomenon called a 'double hop'. The request is sent from the web server, to the SQL server and identity information is passed across to the SQL server. When the sub report is processing the request is again made to the SQL server but at this point the indentity information is lost as it only lasts 2 'hops' This is aparently by design for NTLM authentiation. I have read this can be fixed by using Kerberos authentication but I know nothing about this.
Two solutions that I have come up with seem to work.
1) Change all connection strings to the database to explicity specify the UserID and Password for the SQL connection. This seems to work ok but unless you want to mess about encrypting web configs and connect strings then this isnt really an option.
2) Change the user account the web applications app pool is running under. I changed this account to use the same domain user account that the web application was impersonating. It seems the sub report request is using the identity of the application pool to connect. Changing this then uses the correct user account for the integrated security in the connection string. One thing to note with this method is that I had to change the Anonymous access user in IIS to also use this domain account as a login box was being presented when browsing to the site.
Here is the code for the aspx page that is loading the report. The problem manifests itself in the LocalReport_SubreportProcessing event.
Labels:
ASP,
c#,
ReportViewer
Wednesday, 16 February 2011
Visual Studio 2010 Report Viewer - Object Datasource
I have had no end of issues trying to get the ReportViewer control in Visual Studio to work with business objects in a Web Application (not website)
When I tried to create a new report the datasources window would never pick up my business objects. Here is a solution that I finally got working.
Create a new class library project to host reports. This library can contain the object datasources added by using the Data menu, then Add New Datasource. The reason I could not do this in the Web Application is because this menu item is missing for Web Applications!
Anyway, once you have added the datasources, add a new report to the reporting class library, here it will have access to all of the datasources (my data sources are actually business objects in another class library) This report needs to be set as an embedded resource.
Once you have the report and datasources ready its time to add the ReportViewer to an aspx page. I ran into a problem here when setting the ReportViewer.LocalReport.ReportEmbeddedResource. It looks like the report viewer looks for the resource in the current assembly. Luckily this can be resolved by adding a helper class to the reporting library to set the embedded resource from within the library.
Here is the code for the helper function in the reporting library
and here is how you would use it in the aspx page
Update for .net 4.0 I had an issue where the report would not load after being compiled in .net 4.0. The message read: 'The report definition for report 'xxx.rdlc' has not been specified' even though it was clearly in the assembly. Changing the way the Embeded resource was loaded seemed to fix the problem with the following simple change to the SetReportEmbeddedResource method in the reporting assembly.
When I tried to create a new report the datasources window would never pick up my business objects. Here is a solution that I finally got working.
Create a new class library project to host reports. This library can contain the object datasources added by using the Data menu, then Add New Datasource. The reason I could not do this in the Web Application is because this menu item is missing for Web Applications!
Anyway, once you have added the datasources, add a new report to the reporting class library, here it will have access to all of the datasources (my data sources are actually business objects in another class library) This report needs to be set as an embedded resource.
Once you have the report and datasources ready its time to add the ReportViewer to an aspx page. I ran into a problem here when setting the ReportViewer.LocalReport.ReportEmbeddedResource. It looks like the report viewer looks for the resource in the current assembly. Luckily this can be resolved by adding a helper class to the reporting library to set the embedded resource from within the library.
Here is the code for the helper function in the reporting library
and here is how you would use it in the aspx page
Update for .net 4.0 I had an issue where the report would not load after being compiled in .net 4.0. The message read: 'The report definition for report 'xxx.rdlc' has not been specified' even though it was clearly in the assembly. Changing the way the Embeded resource was loaded seemed to fix the problem with the following simple change to the SetReportEmbeddedResource method in the reporting assembly.
Labels:
ASP,
c#,
ReportViewer
Subscribe to:
Posts (Atom)