Archive of posts filed under the Reporting category.

Webinar (Reporting in Silverlight) download

Recording of the webinar I presented is now up on Magenic web site.  Here is the address: http://www.magenic.com/Default.aspx?tabid=635

Since I am not sure if the sample project is available, I decided to post the zip file with the sample project here.

Please let me know if you have any questions. 

Webinar on reporting in Silverlight

I am presenting a webinar on July 28th on reporting in Silverlight.  This webinar is hosted by my employer, Magenic Technologies.  If you would like to register and watch this event, please follow this link: http://guest.cvent.com/EVENTS/Info/Invitation.aspx?e=6ca01a01-e2bd-4a77-a057-a63ab2208e81

There should be some tome allocated for questions and answers as well.

Thank you.

SSRS and “Unable to load client print control” issue

I was faced with a new (to me) problem today.  As part of testing of a Silverlight application that has SSRS based reports, the client reported an issue on some machines.  They are able to print a report, but clicking on export or print button was causing issues.  The actual error reported from Silverlight application was “Unable to load client print control.”  Apparently this is a know issue caused by a Microsoft issued patch a few month ago.  Here are the details of the issue and links to patches to address it: http://blogs.msdn.com/brianhartman/archive/2008/11/05/client-print-fails-to-load-after-microsoft-update-956391.aspx

Here is a direct link to SQL Server 2008 Report Viewer redistributable package:

http://www.microsoft.com/downloads/details.aspx?FamilyID=bb196d5d-76c2-4a0e-9458-267d22b6aac6&DisplayLang=en

Notes on SSRS deployment

I had to deploy and configure SSRS on Friday.  Getting it configured was an easy part.  Then I had to deploy about 50 reports for testing.  They all shared one data source.  One unfortunate part I found is that you cannot add multiple reports to report server at one time.  So, I had to pick one report at a time to add to SSRS installation.  Once that was done I discovered that data source did not translate properly.  At that point I had to pull up properties of each report and pick a data source yet again.  Once all that was done, I was able to preview reports on SSRS server.

Next step was to configure application server.  In my case I was using ASP.NET based SSRS report viewer.  All my settings were in web.config.  I updated them with the SSRS server location.  However, once I tried to preview a report there, I got a security exception.  What I had to do to resolve that is to note domain\appserver location that my web site was running on.  Then I had to go to SSRS configuration and add a user domain\appserver$ with the role of Browser.  At that point I was able to run and preview the reports.

I have the following notes:

1. Deployment tools (SSRS Management web site) is really lacking.  I was wishing that I could force the person to reply a few hundred reports using that.  I think the tool would be greatly improved after that :-) .

2. Security is quite easy to setup.

Reporting in Silverlight

Reporting in Silverlight environment has one major problem.  Silverlight does not have a report viewer control for either SQL Server reporting services or Crystal reports.  As a result, one has to come up with a workaround that uses existing report viewer controls.  Luckily both Crystal and SSRS have report viewer controls designed for the web.  So, all we need to do is cleanly integrate these controls into a Silverlight application, using browser integration capabilities of Silverlight.  By cleanly, I mean as cleanly as possible :-) .

We have two options to do this.  We can launch another browser window from Silverlight and open the report in new ASPX page or we can embed a new web page that hosts report viewer control inside the web page that hosts Silverlight application.  I will demonstrate these options in the following manner.  Launch a new web page that hosts SSRS report viewer and embed a web page that hosts Crystal reports viewer.

This post builds on top of previous post I had about SSRS implementation.

SSRS implementation

Create ASPX page for report viewer:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ReportViewerForm.aspx.cs"
    Inherits="MyWeb.ReportViewerForm" %>
<!–
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">–>
<%@ Register Assembly="Microsoft.ReportViewer.WebForms, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    Namespace="Microsoft.Reporting.WebForms" TagPrefix="rsweb" %>
<html xmlns="http://www.w3.org/1999/xhtml" style="height:100%;">
<head runat="server">
    <title>Report</title>
</head>
<body>
    <form id="form1" runat="server" style="height:100%" >
    <div style="height:100%" >
        <rsweb:ReportViewer ID="MainReportViewer" runat="server" 
            AsyncRendering="false"
            ProcessingMode="Remote"
            Width="100%"
            ShowExportControls="True"
            ShowFindControls="True"
            ShowParameterPrompts="False"
            ShowPromptAreaButton="False"
            ShowRefreshButton="False"
            BackColor="White">
            <ServerReport DisplayName="MainReport" ReportServerUrl="" />
        </rsweb:ReportViewer>
    </div>
    </form>
</body>
</html>

Create a class to launch a report with the following routine:

public static void LaunchReport(string reportName)
       {
           Uri sourceUri = new Uri(HtmlPage.Document.DocumentUri, Application.Current.Host.Source.ToString().Substring(0, Application.Current.Host.Source.ToString().IndexOf("ClientBin") – 1) + "/ReportViewerForm.aspx?ReportName=" + reportName);

           if (ArePopupsAllowed())
           {
               System.Text.StringBuilder codeToRun = new System.Text.StringBuilder();
               codeToRun.Append("window.open(");
               codeToRun.Append("\"");
               codeToRun.Append(sourceUri.ToString());
               codeToRun.Append("\",");
               codeToRun.Append("\"");
               codeToRun.Append("\",");
               codeToRun.Append("\"");
               codeToRun.Append("width=1100,height=900,scrollbars=yes,menubar=no,toolbar=no,resizable=yes");
               codeToRun.Append("\");");
               try
               {
                   HtmlPage.Window.Eval(codeToRun.ToString());
               }
               catch (Exception ex)
               {
                   // do something               }
           }

           else
              //inform the user that popups need to be enabled
       }

ArePopupsAllowed function tests if popups are allowed in the browser.  Contact me if you need this code.

If you need to pass in parameters, you can easily pass them in as query parameters and parse them inside  ReportViewerForm.aspx just like the report name itself.  Here is code in Load event for this web page:

protected void Page_Load(object sender, EventArgs e)
        {
            MainReportViewer.ProcessingMode = ProcessingMode.Remote;
            MainReportViewer.ServerReport.ReportPath = "/MySSRSReports/" + this.Request.QueryString["ReportName"];
            MainReportViewer.ServerReport.ReportServerUrl = new Uri("http://localhost:8080/ReportServer_SQL2008");
            List<ReportParameter> parameters = new List<ReportParameter>();
            List<string> values = new List<string>();
            values.Add("1");
            values.Add("2");
            values.Add("3");
            ReportParameter oneParamter = new ReportParameter("ParmeterName", values.ToArray());
            parameters.Add(oneParamter);
            MainReportViewer.ServerReport.SetParameters(parameters.ToArray());
            MainReportViewer.ShowParameterPrompts = false;
            MainReportViewer.ServerReport.Refresh();
        }

In the example above I do hard code parameter values, but you get the idea.  Just add your parameters to the URL for ReprotViewerForm and have the form add them to each report.

Of course you also need to create SSRS report as well, but this is a separate subject.

Crystal Reports implementation

First thing is to create a crystal report.  You do have the usual options here to have the report get data directly from database (which is what I am using) or you can write more code to get data into a DataSet for example, using additional .NET classes and pass DataSet to the report.  We have to create a web page here as well to host report viewer.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ReportView.aspx.cs" Inherits="MyApp.Web.ReportView" %>

<%@ Register assembly="CrystalDecisions.Web, Version=10.5.3700.0, Culture=neutral, PublicKeyToken=692fbea5521e1304" namespace="CrystalDecisions.Web" tagprefix="CR" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server"
            AutoDataBind="true" />
    </div>
    </form>
</body>
</html>

Again, you have to put some code to Load event to interpret report name and parameters as well as setup connection information

protected void Page_Load(object sender, EventArgs e)
        {
            string filter = Request.Params["param"];
            CrystalReportViewer1.Attributes.Add("Width", "100%");
            CrystalReportViewer1.Attributes.Add("Width", "100%");

            CrystalDecisions.CrystalReports.Engine.ReportDocument doc = new CrystalDecisions.CrystalReports.Engine.ReportDocument();
            doc.Load(Server.MapPath("MyReport.rpt"));
            foreach (CrystalDecisions.CrystalReports.Engine.Table item in doc.Database.Tables)
            {
                CrystalDecisions.Shared.ConnectionInfo connection = new CrystalDecisions.Shared.ConnectionInfo();
                connection.ServerName = "(local)";
                connection.UserID = "myUser";
                connection.Password = "myPassword";
                connection.DatabaseName = "MyDatabase";
                CrystalDecisions.Shared.TableLogOnInfo logInfo = new CrystalDecisions.Shared.TableLogOnInfo();
                logInfo.ConnectionInfo = connection;
                item.ApplyLogOnInfo(logInfo);
            }

            CrystalDecisions.Shared.ParameterField param = new CrystalDecisions.Shared.ParameterField();

            CrystalDecisions.Shared.ParameterDiscreteValue discreteVal = new CrystalDecisions.Shared.ParameterDiscreteValue();

            discreteVal.Value = filter;
            param.ParameterFieldName = "@Test";
            param.CurrentValues.Add(discreteVal);
            doc.SetParameterValue(param.ParameterFieldName, param.CurrentValues);

            this.CrystalReportViewer1.ReportSource = doc;
        }

So far pretty easy.  How the Silverlight part.  In this case we do not want to have a popup, but instead have our page appear to be a part of Silverlight application.  Let’s see what web page looks like that hosts Silverlight application.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="MyApp.Web._Default" %>

<%@ Register Assembly="System.Web.Silverlight" Namespace="System.Web.UI.SilverlightControls"
    TagPrefix="asp" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="height: 100%;">
<head id="Head1" runat="server">
    <title>Report</title>
</head>
<body style="height: 100%; margin: 0;">
    <form id="form2" runat="server" style="height: 100%;">
    <asp:ScriptManager ID="ScriptManager2" runat="server">
    </asp:ScriptManager>
    <div style="height: 100%;">
        <asp:Silverlight ID="Xaml1" runat="server" Source="~/ClientBin/MySilverlightApp.xap" MinimumVersion="2.0.30523"
            Width="100%" Height="80%" Windowless="true"/>
    </div>
    <iframe id="reportFrame" style="position:absolute; width:0px; height:0px; visibility:hidden"></iframe>
    </form>
</body>

</html>

As you can see, we are using frames in this case to show the report.  Now, the Silverlight code to launch a report:

private void ShowReportButton()        {
            HtmlElement m = HtmlPage.Document.GetElementById("reportFrame");
            if (m != null)
            {
                int top, left, width, height;
                top = (int)this.outerGrid.RowDefinitions[0].ActualHeight;
                left = (int)(this.outerGrid.ColumnDefinitions[0].ActualWidth + this.outerGrid.ColumnDefinitions[1].ActualWidth);
                width = (int)this.outerGrid.ActualWidth – left;
                height = (int)this.outerGrid.ActualHeight – top;
                m.SetStyleAttribute("left", left.ToString());
                m.SetStyleAttribute("top", top.ToString());
                m.SetStyleAttribute("width", width.ToString());
                m.SetStyleAttribute("height", height.ToString());
                m.SetAttribute("src", "ReportView.aspx?param=P");
                m.SetStyleAttribute("visibility", "visible");
            }
        }

In the code about outerGrid is the control that hosts main Silverlight application.  So, since our Silverlight application is located at position (0,0) in the web page (top left corner), we can compute the coordinates and size of report frame by using actual height and width of our Silverlight controls.  Once that is done, we can control the location of the frame and its size to make it appear as part of Silverlight application.  You would also need to listen to resize events of the browser in order to control the size of the report window.

This is my attempt to write a comprehensive guide to Silverlight reporting.