I have a generic ASCX control that uses the Telerik ReportViewer and I want to use that for all my reporting output. But, we run into the problem that we need to provide a Report object (as well as ReportParameters) to that viewer control. Here's how I did it:
1. In my case, I know my report assembly. With just a bit more work, the assembly itself could be passed in, but the following still uses reflection to load the report dynamically.
2. ReportParameters are passed in as "Name=Value;[Name=Value;...]" via a single public property named ReportParametersString. Not particularly extensible, but more than sufficient for my simple needs. I handle type coercion (that is, convert from string that is passed into ASCX parameter to specific type supported by Telerik) using the GetTypeFromReportType method. I also have (of course, we all do...) my own DAL (data access layer) using ADO.NET that can convert from just about any input type to any output type (not documented below).
3. In my report assembly, I purposely provide a dummy empty class to make getting the assembly type that much easier.
4. I assume that the report itself can handle its own datasource needs (more on that in another post--I cracked some code to get my own data connection string without needing to modify web.config which is owned by SharePoint not by me...).
5. My ASCX exposes a ReportName property that can be the absolute or relative class name of the report to load.
So here's the code, hope it's useful!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace ArchG2.Portal.rg2SupportingControls.Misc
{
/// <summary>
/// Generic reporting viewer for any Telerik report
/// </summary>
public partial class TelerikReportViewer : Utils.BaseManageableControl
{
private string _reportName;
private string _prevReportName;
private Telerik.Reporting.IReportDocument _report;
private bool _pageIsLoaded;
private string _reportParametersString;
private Telerik.Reporting.ReportParameterCollection _reportParameters = new Telerik.Reporting.ReportParameterCollection();
private TextBox _txtHeight;
/// <summary>
/// Handle a resize by applying the resize directly to the control
/// </summary>
protected override void resizeHandler()
{
if (Width != Unit.Empty)
{
// set an explicit width to the report
rptvw.Width = Width;
} //if
if (Height != Unit.Empty)
{
// set an explicit height to the report
rptvw.Height = Height;
} //if
} //resizeHandler
/// <summary>
/// Invoked whenever the report must definitely be set in the view control.
/// Will force the view to update / take any other actions.
/// </summary>
private void setReport()
{
// access our stored report
Telerik.Reporting.IReportDocument theReport = Report;
if (theReport == null)
{
LH.warn("No report specified; cannot display");
return;
} //if
// set the report itself in the managed viewer
rptvw.Report = Report;
} //setReport
/// <summary>
/// Given a Telerik reporting type, return a "real" object type so that we can
/// perform type-safe coercion on data values
/// </summary>
/// <param name="reportType">The Telerik report type to convert</param>
/// <returns>The OS type or an exception if this is an unknown type (new Telerik version could introduce this problem)</returns>
private Type GetTypeFromReportType(Telerik.Reporting.ReportParameterType reportType)
{
switch (reportType)
{
case Telerik.Reporting.ReportParameterType.Boolean:
return typeof(Boolean);
case Telerik.Reporting.ReportParameterType.DateTime:
return typeof(DateTime);
case Telerik.Reporting.ReportParameterType.Float:
return typeof(double);
case Telerik.Reporting.ReportParameterType.Integer:
return typeof(int);
case Telerik.Reporting.ReportParameterType.String:
return typeof(string);
default:
throw new ApplicationException(
string.Format("Unknown report type: {0}", reportType.ToString())
);
} //switch
} //GetTypeFromReportType
/// <summary>
/// Use reflection to load a given report object from the ArchG2.Reporting assembly.
/// </summary>
private void loadReport()
{
string flag = "";
try
{
// leverage the Dummy class to get access to the ArchG2.Reporting assembly
flag = "access_archg2_report";
Type typeDummy = typeof(ArchG2.Reporting.Dummy);
System.Reflection.Assembly assemblyArchG2Reporting = typeDummy.Assembly;
// check to see if the user passed in a full path or a short path
flag = "load_report";
string reportToLoad = _reportName;
int idxDot = reportToLoad.IndexOf('.');
if (idxDot <= 0)
{
string prefix = assemblyArchG2Reporting.GetName().Name;
if (idxDot < 0) prefix += ".";
reportToLoad = reportToLoad.Insert(0, prefix);
} //if
LH.debug("Loading report ", reportToLoad, " (", _reportName, ")");
Type typeOfDynamicReport = assemblyArchG2Reporting.GetType(reportToLoad);
// now instantiate an object of the report; must have default ctor
flag = "create_report";
object oReport = Activator.CreateInstance(typeOfDynamicReport);
// validate the report
flag = "validate_report";
Telerik.Reporting.IReportDocument theReport = oReport as Telerik.Reporting.IReportDocument;
if (theReport == null) throw new ApplicationException("Invalid report type: " + typeOfDynamicReport.Name);
// now set any parameters on the report
if (ReportParameters.Count > 0)
{
// get the report list
flag = "access_report_parms";
List<Telerik.Reporting.ReportParameter> theParameters = theReport.ReportParameters.ToList();
// build a dictionary
Dictionary<string, Telerik.Reporting.ReportParameter> theDictionary = new Dictionary<string, Telerik.Reporting.ReportParameter>();
foreach (Telerik.Reporting.ReportParameter parameter in theParameters)
{
theDictionary.Add(parameter.Name.ToUpper(), parameter);
} //foreach
// look up each item from input to the report itself
flag = "iterate_source_parms";
foreach (Telerik.Reporting.ReportParameter sourceParameter in ReportParameters)
{
// the specified source parameter must exist in the report
string key = sourceParameter.Name.ToUpper();
if (!theDictionary.Keys.Contains(key))
{
throw new ApplicationException(
string.Format(
"Source parameter {0} does not exist in report parameters",
sourceParameter.Name
)
);
} //if
Telerik.Reporting.ReportParameter reportParameter = theDictionary[key];
// do simple type coercion
flag = "decode_parm_types";
Type typeReport = GetTypeFromReportType(reportParameter.Type);
Type typeSource = GetTypeFromReportType(sourceParameter.Type);
// shortcut
if (typeReport == typeSource)
{
// set value explicitly
reportParameter.Value = sourceParameter.Value;
continue;
} //if
// simple type coercion (exception on failure)
flag = "convert_" + sourceParameter.Name;
object oParameterValue = Abr.CodeLibrary.DataAccess.DataObject.DbObject(
typeReport, sourceParameter.Value, false
);
reportParameter.Value = oParameterValue;
} //foreach
} //next
// set the report
Report = theReport;
}
catch (Exception ex)
{
LH.error("Problem at ", flag, ": ", ex);
NotifyError("Report Problem: " + ex.Message);
} //try
} //loadReport
/// <summary>
/// The stored report for display within this viewer
/// </summary>
public Telerik.Reporting.IReportDocument Report
{
get { return _report; }
set { _report = value; if (_pageIsLoaded) setReport(); }
} //Report
/// <summary>
/// For control via the ASCX properties, allow a report name to be specified.
/// This name should be the class name of the report in the ArchG2.Reporting
/// module. This property uses reflection to load the report for display by the control.
/// </summary>
public string ReportName
{
get { return _reportName; }
set { _prevReportName = _reportName; _reportName = value; }
} //ReportName
/// <summary>
/// Return reference to the stored report parameter actual list.
/// Useful to access from code (use ReportParametersString from ASCX).
/// </summary>
public Telerik.Reporting.ReportParameterCollection ReportParameters
{
get { return _reportParameters; }
} //ReportParameters
/// <summary>
/// Pass in report parameters as a string; parsed with ; (semicolon)
/// as delimiter. Use Name=Value;[Name=Value;...] format.
/// </summary>
public string ReportParametersString
{
get { return _reportParametersString; }
set
{
string flag = "";
try
{
// anything to do?
flag = "validate_parms";
_reportParametersString = value;
_reportParameters.Clear();
if (string.IsNullOrEmpty(_reportParametersString)) return;
// parse the report parameters
flag = "parse_parms";
string[] parms = _reportParametersString.Split(';');
if (parms.Length == 0) return;
// parse each parameter
flag = "iter_parms";
foreach (string parm in parms)
{
// sanity
if (string.IsNullOrEmpty(parm)) continue;
// validate
int idxEqual = parm.IndexOf('=');
if (idxEqual <= 0)
{
throw new ApplicationException(
string.Format("Parameter {0} should be in format Name=Value", parm)
);
} //if
// construct the parm
string parmName = parm.Substring(0, idxEqual);
string parmValue = parm.Substring(idxEqual + 1);
Telerik.Reporting.ReportParameter reportParm = new Telerik.Reporting.ReportParameter(
parmName, Telerik.Reporting.ReportParameterType.String, parmValue
);
_reportParameters.Add(reportParm);
} //foreach
// if we have already loaded the page,
}
catch (Exception ex)
{
LH.error("Problem at ", flag, ": ", ex);
NotifyError("Report Problem: " + ex.Message);
} //try
} //set
} //ParametersString
/// <summary>
/// Handle export status
/// </summary>
/// <param name="e"></param>
protected override void OnInit(EventArgs e)
{
// create controls
base.OnInit(e);
}
/// <summary>
/// Auto-generated
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
// indicate Page_Load has fired
_pageIsLoaded = true;
// check to see if we need to do a one-time load
if (IsFirstTime)
{
if (Report != null)
{
// user set report explicitly; use that
setReport();
}
else if (!string.IsNullOrEmpty(ReportName))
{
// report name set via ASCX; use that
loadReport();
} //if
// only do this once
SetFirstTime(false);
} //if
// set the height every time from hidden variable
try
{
Unit unitExplicitHeight = Unit.Parse(hdnExplicitHeight.Value);
if (unitExplicitHeight != Unit.Empty && unitExplicitHeight.Value > 0)
{
// safety
if (unitExplicitHeight.Value < 100)
{
unitExplicitHeight = Unit.Parse("100px");
} //if
Height = unitExplicitHeight;
} //if
}
catch
{
} //try
}
/// <summary>
/// Put in our own control height (page height)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void rptvw_Init(object sender, EventArgs e)
{
string flag = "";
try
{
// find the toolbar
flag = "locate_toolbar";
Control ctlToolbar = rptvw.FindControl("ReportToolbar");
if (ctlToolbar == null) throw new ApplicationException("No toolbar; new Telerik version?");
// this is reverse engineered from telerik; might work
flag = "create_controls";
Table tblHeight = new Table();
tblHeight.ID = "PageHeight";
tblHeight.CssClass = "ReportToolbarGroup";
Abr.CodeLibrary.Utils.Global.MergeCssStyleAttributes(tblHeight.Attributes, "float:left;");
tblHeight.CellPadding = 0;
tblHeight.CellSpacing = 0;
TableRow rowHeight = new TableRow();
rowHeight.ID = "row";
TableCell cellHeight = new TableCell();
cellHeight.ID = "cell";
Label lblHeight = new Label();
lblHeight.ID = "lblHeight";
lblHeight.AssociatedControlID = "txtHeight";
lblHeight.Text = "Height: ";
_txtHeight = new TextBox();
_txtHeight.ID = "txtHeight";
_txtHeight.Width = Unit.Parse("5em");
LinkButton btnResize = new LinkButton();
btnResize.ID = "btnResize";
btnResize.Text = "Resize";
btnResize.Click += new EventHandler(btnResize_Click);
flag = "add_controls";
cellHeight.Controls.Add(lblHeight);
cellHeight.Controls.Add(_txtHeight);
cellHeight.Controls.Add(btnResize);
rowHeight.Cells.Add(cellHeight);
tblHeight.Rows.Add(rowHeight);
ctlToolbar.Controls.Add(tblHeight);
}
catch (Exception ex)
{
LH.error("Problem at ", flag, ": ", ex);
NotifyError("Report: " + ex.Message);
} //try
}
/// <summary>
/// Used to capture explicit resize
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void btnResize_Click(object sender, EventArgs e)
{
hdnExplicitHeight.Value = _txtHeight.Text;
Height = Unit.Parse(_txtHeight.Text);
}
} //TelerikReportViewer
} //namespace