Click here to Skip to main content
Click here to Skip to main content
Go to top

Silverlight Report Viewer using View Model (MVVM)

, 13 Dec 2011
Rate this:
Please Sign up or sign in to vote.
Viewing .rdlc reports from Silverlight using View Model (MVVM)

Viewing And Printing Reports in a Silverlight Application

Reports are an important part of a Silverlight Line of Business (LOB) application. While Silverlight 4 does provide printing capabilities, multi-page printing is still challenging. In addition, a lot of businesses have already invested considerable resources into learning to produce reports using the report designer in Visual Studio.

While there are some 3rd party controls that will display a report in Silverlight, they cost money. Also, some require you to use the full Microsoft SQL Server Reporting Services (SSRS), and that may not be an option for a lot of people. An .rdlc report created with Visual Studio does not require SSRS.

The method we will demonstrate in this article was developed by Sushant Pandey. He describes it here. The difference in this implementation is that we will use Behaviors to make it fully MVVM compliant (he uses code behind in his implementation).

Printing A Report

We will start with the Simple Silverlight Configurator/Pivot (View Model/MVVM) I published a few months ago.

The application allows the user to change the Gender, Weight, and Age selectors, and it dynamically filters the list of people.

The application has been enhanced to allow the user to click the Show Report Viewer button and the report will show in a popup window.

If they click Print PDF Report, they will see a PDF report in a popup window.

I actually prefer the Report Viewer because the user sees an animation while waiting for the report to render.

Creating the Report

The first step is to add a Report to the project.

The project already contains the following web service:

[WebMethod]
public List<Person> SearchPeople(string Gender, int AgeHigh, int AgeLow,
    int WeightHigh, int WeightLow)
{
    List<Person> colPeople = new List<Person>();
    
    var result = from objPeople in People.GetPeople().AsQueryable()
                 where objPeople.Age <= AgeHigh
                 where objPeople.Age >= AgeLow
                 where objPeople.Weight <= WeightHigh
                 where objPeople.Weight >= WeightLow
                 select objPeople;
                 
    if (Gender != "All")
    {
        colPeople = (from finalresult in result
                     where finalresult.Gender == Gender
                     select finalresult).ToList();
    }
    else
    {
        colPeople = result.ToList();
    }
    
    return colPeople;
}

We are able to select that web service as a dataset for the report.

After designing the report, we place a report control on a .aspx page.

The following code, in the code behind for the page, is used to accept report parameters, and to display the Report Viewer Control (ShowReportViewer()), or to render a .pdf version (RenderReport()):

public partial class Report : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        // If parameters are passed just render report
        if
            (
            Request.QueryString["Gender"] != null &&
            Request.QueryString["AgeHigh"] != null &&
            Request.QueryString["AgeLow"] != null &&
            Request.QueryString["WeightHigh"] != null &&
            Request.QueryString["WeightLow"] != null
            )
        {
            if (Request.QueryString["ShowReportViewer"] == "False")
            {
                RenderReport();
            }
            else
            {
                if (!Page.IsPostBack)
                {
                    ShowReportViewer(); 
                }
            }
        }
    }
    
    #region RenderReport
    private void RenderReport()
    {
        // CheckBox to see if there is any data
        if (LoadData().Rows.Count > 0)
        {
            LocalReport localReport = new LocalReport();
            localReport.ReportPath = Server.MapPath("~/Report1.rdlc");
            
            ReportDataSource reportDataSource = 
		new ReportDataSource("DataSet1", LoadData());
            localReport.DataSources.Add(reportDataSource);
            
            string reportType = "pdf";
            string mimeType = "application/pdf";
            string encoding;
            string fileNameExtension;
            
            //The DeviceInfo settings should be changed based on the reportType
            //http://msdn2.microsoft.com/en-us/library/ms155397.aspx
            string deviceInfo =
            "<DeviceInfo>" +
            " <OutputFormat>PDF</OutputFormat>" +
            " <PageWidth>8.5in</PageWidth>" +
            " <PageHeight>11in</PageHeight>" +
            " <MarginTop>0.5in</MarginTop>" +
            " <MarginLeft>1in</MarginLeft>" +
            " <MarginRight>1in</MarginRight>" +
            " <MarginBottom>0.5in</MarginBottom>" +
            "</DeviceInfo>";
            
            Warning[] warnings;
            string[] streams;
            byte[] renderedBytes;
            
            //Render the report
            renderedBytes = localReport.Render(
            reportType,
           deviceInfo,
           out mimeType,
           out encoding,
           out fileNameExtension,
           out streams,
           out warnings);
           
            //Clear the response stream and write the bytes to the outputstream
            //Set content-disposition to "attachment" 
            //so that user is prompted to take an action
            //on the file (open or save)
            Response.Clear();
            Response.ContentType = mimeType;
            //Response.AddHeader("content-disposition", 
		"attachment; filename=foo." + fileNameExtension);
            Response.BinaryWrite(renderedBytes);
            Response.End();
        }
        else
        {
            // There were no records returned
            Response.Clear();
            //Response.AddHeader("content-disposition", 
		"attachment; filename=foo." + fileNameExtension);
            Response.Write("No Data");
            Response.End();
        }
    }
    #endregion
    
    #region ShowReportViewer
    private void ShowReportViewer()
    {
        this.ReportViewer1.ProcessingMode = ProcessingMode.Local;
        this.ReportViewer1.LocalReport.ReportPath = Server.MapPath("~/Report1.rdlc");
        ReportViewer1.LocalReport.DataSources.Add(
           new ReportDataSource("DataSet1", LoadData()));
    }
    #endregion

The following method provides data for the report at run-time:

#region LoadData
private DataTable LoadData()
{
    string Gender = Convert.ToString(Request.QueryString["Gender"]);
    int AgeHigh = Convert.ToInt32(Request.QueryString["AgeHigh"]);
    int AgeLow = Convert.ToInt32(Request.QueryString["AgeLow"]);
    int WeightHigh = Convert.ToInt32(Request.QueryString["WeightHigh"]);
    int WeightLow = Convert.ToInt32(Request.QueryString["WeightLow"]);
    
    var result = from objPeople in People.GetPeople().AsQueryable()
                 where objPeople.Age <= AgeHigh
                 where objPeople.Age >= AgeLow
                 where objPeople.Weight <= WeightHigh
                 where objPeople.Weight >= WeightLow
                 select objPeople;
                 
    if (Gender != "All")
    {
        result = from finalresult in result
                 where finalresult.Gender == Gender
                 select finalresult;
    }
    
    Utility objUtility = new Utility();
    DataTable dt = objUtility.LINQToDataTable(result);
    
    return dt.DefaultView.Table;
}
#endregion

Note, that it uses a class LINQToDataTable, that converts the Linq to SQL query into a DataTable that the report needs.

Also note that we don't actually pass parameters to the Report Viewer control. With a .rdlc report, we simply provide the data for the report. We must filter the Linq query to filter the report.

It may also seem odd that we designed the report directly against the web service method, but we have to supply the data in the code behind. You can see this site for answers as to why the Report Viewer control has been designed this way: http://www.gotreportviewer.com.

The Silverlight Project

In the Silverlight project, we add ReportPDFURL and ReportViewerURL properties to the View Model. We also add a SetReportUrls method that is triggered whenever a supporting property is changed:

private void SetReportUrls()
{
    // Set the Report URLs
    ReportViewerURL = String.Format("{0}Gender={1}&AgeHigh={2}&AgeLow={3}&
			WeightHigh={4}&WeightLow={5}&ShowReportViewer=True",
            GetBaseAddress(), Gender, AgeHigh.ToString(), AgeLow.ToString(), 
			WeightHigh.ToString(), WeightLow.ToString());

    ReportPDFURL = String.Format("{0}Gender={1}&AgeHigh={2}&AgeLow={3}&
			WeightHigh={4}&WeightLow={5}&ShowReportViewer=False",
        GetBaseAddress(), Gender, AgeHigh.ToString(), AgeLow.ToString(), 
			WeightHigh.ToString(), WeightLow.ToString());
} 

We create the following Behavior that will open up the HTML Popup window:

namespace Behaviors
{
    [System.ComponentModel.Description("Opens an HTML Window")]
    public class HTMLPopupWindow : TargetedTriggerAction<Button>, INotifyPropertyChanged
    {
        #region PopupURL
        public static readonly DependencyProperty PopupURLProperty = 
				DependencyProperty.Register("PopupURL",
            typeof(string), typeof(HTMLPopupWindow), null);
        public string PopupURL
        {
            get
            {
                return (string)base.GetValue(PopupURLProperty);
            }
            set
            {
                base.SetValue(PopupURLProperty, value);
                this.NotifyPropertyChanged("PopupURL");
            }
        }
        #endregion

        #region PopupWidth
        public static readonly DependencyProperty PopupWidthProperty = 
				DependencyProperty.Register("PopupWidth",
            typeof(int), typeof(HTMLPopupWindow), null);
        public int PopupWidth
        {
            get
            {
                return (int)base.GetValue(PopupWidthProperty);
            }
            set
            {
                base.SetValue(PopupWidthProperty, value);
                this.NotifyPropertyChanged("PopupWidth");
            }
        }
        #endregion

        #region PopupHeight
        public static readonly DependencyProperty PopupHeightProperty = 
				DependencyProperty.Register("PopupHeight",
            typeof(int), typeof(HTMLPopupWindow), null);
        public int PopupHeight
        {
            get
            {
                return (int)base.GetValue(PopupHeightProperty);
            }
            set
            {
                base.SetValue(PopupHeightProperty, value);
                this.NotifyPropertyChanged("PopupHeight");
            }
        }
        #endregion

        protected override void OnAttached()
        {
            base.OnAttached();
        }

        protected override void Invoke(object parameter)
        {
            if (true == HtmlPage.IsPopupWindowAllowed)
            {
                System.Text.StringBuilder codeToRun = new System.Text.StringBuilder();
                codeToRun.Append("window.open(");
                codeToRun.Append("\"");
                codeToRun.Append(string.Format("{0}", PopupURL));
                codeToRun.Append("\",");
                codeToRun.Append("\"");
                codeToRun.Append("\",");
                codeToRun.Append("\"");
                codeToRun.Append("width=" + PopupWidth.ToString() + 
				",height=" + PopupHeight.ToString());
                codeToRun.Append(",scrollbars=yes,menubar=no,toolbar=no,resizable=yes");
                codeToRun.Append("\");");
                try
                {
                    HtmlPage.Window.Eval(codeToRun.ToString());
                }
                catch
                {
                    MessageBox.Show("You must enable popups to view reports. 
			Safari browser is not supported.",
                        	"Error", MessageBoxButton.OK);
                }
            }
            else
            {
                MessageBox.Show("You must enable popups to view reports. 
			Safari browser is not supported.",
                    	"Error", MessageBoxButton.OK);
            }
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
        }

        // Utility

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion
    }
}

Next, we add buttons to the View.

We add the HTMLPopupWindow Behavior to the Buttons.

We then bind the PopupURL to the appropriate property in the View Model, and set the other parameters.

Not Really Printing In Silverlight

The key thing with this implementation is that we are not actually printing "inside" Silverlight. However, the end user will most likely not care. They will have the functionality they need. As a developer, this method should allow you to deliver a professional solution at a reasonable cost.

Deployment

You will need to use ASP.NET 4.0, and install the Microsoft Report Viewer 2010 Redistributable Package on the web server that displays the report.

History

  • 24th October, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

defwebserver
Software Developer (Senior) http://ADefWebserver.com
United States United States
Michael Washington is a Microsoft MVP. He is a ASP.NET and
C# programmer.
He is the founder of
LightSwitchHelpWebsite.com

He has a son, Zachary and resides in Los Angeles with his wife Valerie.

He is the Author of:
Follow on   Twitter

Comments and Discussions

 
QuestionWow,, PinmemberDrimcaster14-Aug-14 3:08 
AnswerRe: Wow,, Pinmemberdefwebserver14-Aug-14 7:53 
GeneralMy vote of 5 Pinmemberjownby22-Aug-12 6:25 
QuestionDeployemt to Azure is hanging PinmemberMivoat20-Apr-12 5:27 
AnswerRe: Deployemt to Azure is hanging Pinmemberdefwebserver20-Apr-12 5:33 
Questionthank you PinmemberSim_Card27-Feb-12 4:11 
QuestionYou get my 5 BUT there is one thing lacking in your article and that is.... PinmemberBill SerGio, The Infomercial King13-Dec-11 7:52 
AnswerRe: You get my 5 BUT there is one thing lacking in your article and that is.... Pinmvpdefwebserver13-Dec-11 9:46 
Questioni have a problem in using it Pinmemberwabi6669-Oct-11 17:44 
AnswerRe: i have a problem in using it Pinmvpdefwebserver9-Oct-11 17:55 
GeneralRe: i have a problem in using it Pinmemberwabi6669-Oct-11 18:51 
GeneralRe: Silverlight Report Viewer using View Model (MVVM) Good Job! PinmemberMember 165621424-May-11 7:35 
GeneralRe: Silverlight Report Viewer using View Model (MVVM) Good Job! Pinmvpdefwebserver24-May-11 7:36 
GeneralMy vote 5 and Thanks Pinmemberjadughar9-May-11 2:26 
GeneralRe: My vote 5 and Thanks Pinmvpdefwebserver9-May-11 2:31 
GeneralProblem with the example Pinmemberr0lan210-Mar-11 4:37 
GeneralRe: Problem with the example Pinmvpdefwebserver9-May-11 2:50 
GeneralOBB Pinmemberaris1971-200831-Oct-10 5:45 
GeneralRe: OBB Pinmemberdefwebserver2-Nov-10 7:03 
GeneralRe: OBB Pinmemberaris1971-20082-Nov-10 10:41 
GeneralRe: OBB Pinmemberdefwebserver4-Nov-10 8:01 
As I understand it, the Control from Spursoft Solutions is actually just a wrapper around the Silverlight webbrowser control so you just figured out by yourself Smile | :)
GeneralMy vote of 5 PinmemberMarcelo Ricardo de Oliveira27-Oct-10 5:47 
GeneralRe: My vote of 5 Pinmemberdefwebserver27-Oct-10 6:17 
GeneralIt's not a really sl report viewer Pinmembercjc00726-Oct-10 23:34 
GeneralRe: It's not a really sl report viewer Pinmemberdefwebserver27-Oct-10 2:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140926.1 | Last Updated 13 Dec 2011
Article Copyright 2010 by defwebserver
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid