Migrate WebForms report viewer to html5

1 Answer 9 Views
.NET Framework Programming Report Book Report Viewer - HTML5 Report Viewer - HTML5 WebForms Upgrade
Mark
Top achievements
Rank 1
Mark asked on 14 May 2025, 01:21 AM

Hi,

I'm trying to migrate a project using the legacy WebForms report viewer to html5.

Now our use case requires me to build a report book at runtime, pass different parameters in per report and then add that report to the book. Once I collected all the reports in the book I want to show that specific book in the html5 viewer.

However I'm unsure as to how I can achieve this since the ReportSource doesn't allow me to add the instanciated book in.

Here is a quick example of roughly what I'm trying to do in this case:

Dim rs As New Telerik.ReportViewer.Html5.WebForms.ReportSource()
Dim multiQuoteBook As New Telerik.Reporting.ReportBook()
For Each quoteId In quotes
    Dim localQuoteReport As New Telerik.Reporting.TypeReportSource() 
    localQuoteReport.TypeName = GetType(Reports.QuoteReport).AssemblyQualifiedName
    localQuoteReport.Parameters.Add("LoginEntryId", LoginEntryid)
    localQuoteReport.Parameters.Add("DocumentId", quoteId)
    multiQuoteBook.ReportSources.Add(localQuoteReport)
Next
' How do I assign "multiQuoteBook" to the ReportSource "rs" ?
ReportViewer1.ReportSource = rs

Any input is appreciated.

I found this thread here: ReportBook using HTML5 Viewer in Reporting | Telerik Forums where the dev basically gave up on it as it seemed to hard. It's quite strange that I'm struggling with the same issue.

1 Answer, 1 is accepted

Sort by
0
Dimitar
Telerik team
answered on 15 May 2025, 12:09 PM

Hi Mark,

Thank you for the shared information about your scenario!

I had a look at the linked forum thread and while the scenario is very similar to your case, it seems that the user encountered problems not directly related to the initial task of displaying a Report Book but rather some kind of an issue with the Reporting REST Service and the data used in the reports.

While I agree that displaying a dynamically created Report Book in the HTML5 WebForms Report Viewer can be harder than displaying a normal report, I will share with you an approach that should make it very similar to how it was with the legacy viewer, with a small difference.

Since the report book needs to be "built" or in other words - it is dynamically created, it will be necessary to implement and use a custom IReportSourceResolver. When working with a custom resolver, it is recommended to use the IdentifierType.CustomReportSource option.

For example, let's say that you have added the HTML5 ASP.NET Web Forms Report Viewer to an ASPX page like this:

<body>
    <form runat="server">

        <telerik:ReportViewer ScaleMode="FitPageWidth" Width="" Height="" EnableAccessibility="false"
            ID="reportViewer1"
            runat="server">
        </telerik:ReportViewer>
    </form>
</body>

In the ASPX.CS file, in the code behind, you can define and set the report source to the report viewer. For example:

namespace CSharp.NetFramework.WebFormsIntegrationDemo
{
    using System;
    using Telerik.Reporting.Examples.CSharp;
    using Telerik.ReportViewer.Html5.WebForms;

    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                // define the type of the ReportSource to CustomReportSource
                var reportSource = new Telerik.ReportViewer.Html5.WebForms.ReportSource();
                reportSource.IdentifierType = IdentifierType.CustomReportSource;

                // set the report source to the type of the report
                reportSource.Identifier = "Invoice,Dashboard";

                // provide values to the report parameters
                // reportSource.Parameters.Add("ParameterName", "Value");

                // add the report source to the ReportViewer
                this.reportViewer1.ReportSource = reportSource;
            }
        }
    }
}

Notice how I set the identifier to the string "Invoice,Dashboard". This is because, I assume, you wish to dynamically decide what reports will be rendered in the report book and it is one way to do it.

In the custom resolver, I will have this string as the first argument to the Resolve method, and it can be split into a string[] so that you can use the values to dynamically decide an instance of what report to create. For example:

using System;
using System.Collections.Generic;
using Telerik.Reporting.Services;
using Telerik.Reporting;
using Telerik.Reporting.Examples.CSharp;

namespace CSharp.NetFramework.WebFormsIntegrationDemo
{
    public class CustomReportSourceResolver : IReportSourceResolver
    {
        public ReportSource Resolve(string reportId, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
        {
            var reportNames = reportId.Split(',');
            var reportBook = new Telerik.Reporting.ReportBook();

            foreach (var name in reportNames)
            {
                switch(name)
                {
                    case "Invoice":
                        reportBook.ReportSources.Add(new InstanceReportSource()
                        {
                            ReportDocument = new Invoice()
                        });
                        break;
                    case "Dashboard":
                        reportBook.ReportSources.Add(new InstanceReportSource()
                        {
                            ReportDocument = new Invoice()
                        });
                        break;
                    default:
                        throw new ArgumentException($"Unknown report name: {name}");
                }
            }

            return new InstanceReportSource
            {
                ReportDocument = reportBook
            };
        }
    }
}

When all of the above is configured, all that is left is to tell the Reporting service to use the custom resolver which you can do from inside the ReportsController, for example:

namespace CSharp.NetFramework.WebFormsIntegrationDemo.Controllers
{
    using System.IO;
    using System.Net;
    using System.Net.Mail;
    using System.Web;
    using Telerik.Reporting.Cache.File;
    using Telerik.Reporting.Services;
    using Telerik.Reporting.Services.WebApi;

    public class ReportsController : ReportsControllerBase
    {
        static readonly ReportServiceConfiguration configurationInstance;

        static ReportsController()
        {      
            // Configure dependencies for ReportsController.
            // For detailed reports service configuration take a look at the ReportingRestServiceCorsDemo example.
            configurationInstance = AddTelerikReporting("Html5DemoApp", new FileStorage(), new CustomReportSourceResolver());
        }

        public ReportsController()
        {
            this.ReportServiceConfiguration = configurationInstance;
        }
    }
}

With all of this implemented, I would say that the main difference between the legacy and current WebForms Report Viewer is that with the current it is necessary to extract the reports-related logic in the custom resolver while in the legacy the code between the code and viewer was "closer" to one another. The reason for this is that the current WebForms viewer uses the service to render the reports so it never interacts with them directly, thus this difference.

For more information on the topic, I highly recommend taking a look at the following articles as well:

I hope that the provided information and examples are helpful. Please do not hesitate to let me know if I can assist with anything else.

Regards,
Dimitar
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Mark
Top achievements
Rank 1
commented on 18 May 2025, 10:40 PM

Hi Dimitar,

Can I ask how I tie the ReportsController to the ReportViewer? Or does the ReportViewer always try to connect to the ReportsController api?

Also, in regard to the reportId: you're basically suggesting using the reportId as a custom field/ control mechanism to tell the Resolver how to handle a specific request. For my case that would mean I would probably have to create a class to handle the payload that I'm sending. 

Something along the lines of this:
{
  [
    {
      "reportType": "invoice",
      "ids": [
        "123",
        "456"
      ]
    },
    {
      "reportType": "quote",
      "ids": [
        "q-999",
        "q-63",
        "q-8430"
      ]
    },
    {
      "reportType": "credit",
      "ids": [
        "CR-3672"
      ]
    }
  ]
}
Then all the logic to convert the data into the report will sit inside the resolver.
Dimitar
Telerik team
commented on 20 May 2025, 02:25 PM

Hi Mark,

Indeed, the HTML5-based Report Viewers always make requests to the Reporting API as they do not have an embedded engine for rendering the reports, the server takes care of that and sends the generated document to the client.

For a more detailed explanation of this process, you may see The Communication between the Viewer and the REST Service Explained - Telerik Reporting article.

Regarding the reports and their parameters, if there isn't another way to access this information on the server, in the custom IReportSourceResolver, it would indeed be necessary to send the information through the viewer.

So the approach that you are thinking of using is correct - a custom class with the data structure of the reports and parameters, which you serialize and send as a JSON string through the ReportSource.Identifier property, and deserialize in the custom resolver so that you can read the data.

 

Tags
.NET Framework Programming Report Book Report Viewer - HTML5 Report Viewer - HTML5 WebForms Upgrade
Asked by
Mark
Top achievements
Rank 1
Answers by
Dimitar
Telerik team
Share this question
or