Let me say that you can greatly improve the XSLT. In a custom solution where I've implemented this, I have a Page Header with the report title, column widths that expand to fit the whole page and a Footer. But that solution is highly customized and not fit as a generic report generator. Just keep in mind that you may adjust the XSLT to fit your needs.
First the code for C# helper class...
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;
namespace x
{
/// <summary>
/// Summary description for RdlcEngine
/// </summary>
public class RdlcEngine
{
public RdlcEngine()
{
//
// TODO: Add constructor logic here
//
}
#region bind control
public static void BindControl(Microsoft.Reporting.WebForms.ReportViewer rv, DataSet data, string name)
{
string virtualRldc = HRWebsite.RdlcEngine.BuildRDLC(data, name);
BindControl(rv, data, name, virtualRldc);
}
public static void BindControl(Microsoft.Reporting.WebForms.ReportViewer rv, DataSet data, string name, string virtualRldc)
{
rv.ProcessingMode = Microsoft.Reporting.WebForms.ProcessingMode.Local;
Microsoft.Reporting.WebForms.ReportDataSource rds = new Microsoft.Reporting.WebForms.ReportDataSource();
rds.Name = name + "_Table";
rds.Value = data.Tables[0];
Microsoft.Reporting.WebForms.LocalReport r = rv.LocalReport;
r.ReportPath = virtualRldc;
r.DataSources.Add(rds);
}
#endregion
#region RDLC
/// <summary>
/// constructs a simple report RDLC file based on a DataSet
/// </summary>
/// <param name="data"></param>
/// <param name="name"></param>
/// <returns></returns>
public static string BuildRDLC(DataSet data, string name)
{
// establish some file names
string virtualXslt = "xslt/rdlc.xsl";
string virtualRdlc = "rdlc/" + name + ".rdlc";
string virtualSchema = "rdlc/" + name + ".schema";
// set the NAME on the DataSet
// this may or may not be necessary, but the RDLC and DataSet
// will both have the same name if this is done.
data.DataSetName = name;
// write the DataSet Schema to a file
// we should be passing a DataSet with only one DataTable
// the rdlc.xsl does not account for multiple DataTables
string physicalSchema = HttpContext.Current.Server.MapPath(virtualSchema);
data.WriteXmlSchema(physicalSchema);
// load the DataSet schema in a DOM
XmlDocument xmlDomSchema = new XmlDocument();
xmlDomSchema.Load(physicalSchema);
// append the NAME to the schema DOM
// this is so we can pick it up in the rdlc.xsl
// and use it
xmlDomSchema.DocumentElement.SetAttribute("Name", name + "_Table");
// transform the Schema Xml with rdlc.xsl
string physicalXslt = HttpContext.Current.Server.MapPath(virtualXslt);
string xml = HRWebsite.General.TransformXml(xmlDomSchema.OuterXml, physicalXslt);
// save off the resultng RDLC file
string physicalRdlc = HttpContext.Current.Server.MapPath(virtualRdlc);
XmlDocument xmlDomRdlc = new XmlDocument();
xmlDomRdlc.LoadXml(xml);
xmlDomRdlc.Save(physicalRdlc);
// return the virtual path of the RDLC file
// this is needed by the asp:ReportViewer
return virtualRdlc;
}
#endregion
#region Render
public static byte[] RenderReport(DataSet data, string name, string type)
{
Microsoft.Reporting.WebForms.ReportDataSource rds = new Microsoft.Reporting.WebForms.ReportDataSource();
rds.Name = name + "_Table";
rds.Value = data.Tables[0];
string virtualRdlc = BuildRDLC(data, name);
Microsoft.Reporting.WebForms.LocalReport lr = new Microsoft.Reporting.WebForms.LocalReport();
lr.ReportPath = HttpContext.Current.Server.MapPath(virtualRdlc);
lr.DataSources.Add(rds);
return RenderReport(lr, name, type);
}
public static byte[] RenderReport(Microsoft.Reporting.WebForms.LocalReport lr, string name, string type)
{
string extension = string.Empty;
string mimeType = string.Empty;
switch (type)
{
case "PDF":
extension = "pdf";
mimeType = "application/pdf";
break;
case "Excel":
extension = "xls";
mimeType = "application/vnd.excel";
break;
case "Image":
extension = "emf";
mimeType = "application/image";
break;
default:
throw new Exception("Unrecognized type: " + type + ". Type must be PDF, Excel or Image.");
}
//The DeviceInfo settings should be changed based on the reportType
//http://msdn2.microsoft.com/en-us/library/ms155397.aspx
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append("<DeviceInfo>");
sb.Append("<OutputFormat>");
sb.Append(type);
sb.Append("</OutputFormat>");
sb.Append("<PageWidth>11in</PageWidth>");
sb.Append("<PageHeight>8.5in</PageHeight>");
sb.Append("<MarginTop>1in</MarginTop>");
sb.Append("<MarginLeft>1in</MarginLeft>");
sb.Append("<MarginRight>1in</MarginRight>");
sb.Append("<MarginBottom>1in</MarginBottom>");
sb.Append("</DeviceInfo>");
string deviceInfo = sb.ToString();
string encoding;
Microsoft.Reporting.WebForms.Warning[] warnings;
string[] streams;
byte[] result;
//Render the report
result = lr.Render(
type,
deviceInfo,
out mimeType,
out encoding,
out extension,
out streams,
out warnings);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = mimeType;
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + name + "." + extension);
HttpContext.Current.Response.BinaryWrite(result);
HttpContext.Current.Response.End();
return result;
}
#endregion
}
}
Next, the XSLT....
<?xml version="1.0"?>
<!-- Stylesheet for creating ReportViewer RDLC documents -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition"
>
<xsl:variable name="mvarName" select="/xs:schema/@Name"/>
<xsl:variable name="mvarFontSize">8pt</xsl:variable>
<xsl:variable name="mvarFontWeight">500</xsl:variable>
<xsl:variable name="mvarFontWeightBold">700</xsl:variable>
<xsl:template match="/">
<xsl:apply-templates select="/xs:schema/xs:element/xs:complexType/xs:choice/xs:element/xs:complexType/xs:sequence">
</xsl:apply-templates>
</xsl:template>
<xsl:template match="xs:sequence">
<Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition">
<BottomMargin>1in</BottomMargin>
<RightMargin>1in</RightMargin>
<LeftMargin>1in</LeftMargin>
<TopMargin>1in</TopMargin>
<InteractiveHeight>11in</InteractiveHeight>
<InteractiveWidth>8.5in</InteractiveWidth>
<Width>6.5in</Width>
<Language>en-US</Language>
<rd:DrawGrid>true</rd:DrawGrid>
<rd:SnapToGrid>true</rd:SnapToGrid>
<rd:ReportID>7358b654-3ca3-44a0-8677-efe0a55c7c45</rd:ReportID>
<xsl:call-template name="BuildDataSource">
</xsl:call-template>
<xsl:call-template name="BuildDataSet">
</xsl:call-template>
<Body>
<Height>0.50in</Height>
<ReportItems>
<Table Name="table1">
<DataSetName><xsl:value-of select="$mvarName" /></DataSetName>
<Top>0.5in</Top>
<Height>0.50in</Height>
<Header>
<TableRows>
<TableRow>
<Height>0.25in</Height>
<TableCells>
<xsl:apply-templates select="xs:element" mode="HeaderTableCell">
</xsl:apply-templates>
</TableCells>
</TableRow>
</TableRows>
</Header>
<Details>
<TableRows>
<TableRow>
<Height>0.25in</Height>
<TableCells>
<xsl:apply-templates select="xs:element" mode="DetailTableCell">
</xsl:apply-templates>
</TableCells>
</TableRow>
</TableRows>
</Details>
<TableColumns>
<xsl:apply-templates select="xs:element" mode="TableColumn">
</xsl:apply-templates>
</TableColumns>
</Table>
</ReportItems>
</Body>
</Report>
</xsl:template>
<xsl:template name="BuildDataSource">
<DataSources>
<DataSource Name="DummyDataSource">
<ConnectionProperties>
<ConnectString/>
<DataProvider>SQL</DataProvider>
</ConnectionProperties>
<rd:DataSourceID>84635ff8-d177-4a25-9aa5-5a921652c79c</rd:DataSourceID>
</DataSource>
</DataSources>
</xsl:template>
<xsl:template name="BuildDataSet">
<DataSets>
<DataSet Name="{$mvarName}">
<Query>
<rd:UseGenericDesigner>true</rd:UseGenericDesigner>
<CommandText/>
<DataSourceName>DummyDataSource</DataSourceName>
</Query>
<Fields>
<xsl:apply-templates select="xs:element" mode="Field">
</xsl:apply-templates>
</Fields>
</DataSet>
</DataSets>
</xsl:template>
<xsl:template match="xs:element" mode="Field">
<xsl:variable name="varFieldName">
<xsl:value-of select="@name" />
</xsl:variable>
<xsl:variable name="varDataType">
<xsl:choose>
<xsl:when test="@type='xs:int'">System.Int32</xsl:when>
<xsl:when test="@type='xs:string'">System.String</xsl:when>
<xsl:when test="@type='xs:dateTime'">System.DateTime</xsl:when>
<xsl:when test="@type='xs:boolean'">System.Boolean</xsl:when>
</xsl:choose>
</xsl:variable>
<Field Name="{$varFieldName}">
<rd:TypeName><xsl:value-of select="$varDataType"/></rd:TypeName>
<DataField><xsl:value-of select="$varFieldName"/></DataField>
</Field>
</xsl:template>
<xsl:template match="xs:element" mode="HeaderTableCell">
<xsl:variable name="varFieldName">
<xsl:value-of select="@name" />
</xsl:variable>
<TableCell>
<ReportItems>
<Textbox Name="textbox{position()}">
<rd:DefaultName>textbox<xsl:value-of select="position()"/>
</rd:DefaultName>
<Value><xsl:value-of select="$varFieldName"/></Value>
<CanGrow>true</CanGrow>
<ZIndex>7</ZIndex>
<Style>
<TextAlign>Center</TextAlign>
<PaddingLeft>2pt</PaddingLeft>
<PaddingBottom>2pt</PaddingBottom>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<FontSize><xsl:value-of select="$mvarFontSize"/></FontSize>
<FontWeight><xsl:value-of select="$mvarFontWeightBold"/></FontWeight>
<BackgroundColor>#000000</BackgroundColor>
<Color>#ffffff</Color>
<BorderColor>
<Default>#ffffff</Default>
</BorderColor>
<BorderStyle>
<Default>Solid</Default>
</BorderStyle>
</Style>
</Textbox>
</ReportItems>
</TableCell>
</xsl:template>
<xsl:template match="xs:element" mode="DetailTableCell">
<xsl:variable name="varFieldName">
<xsl:value-of select="@name" />
</xsl:variable>
<TableCell>
<ReportItems>
<Textbox Name="{$varFieldName}">
<rd:DefaultName><xsl:value-of select="$varFieldName"/></rd:DefaultName>
<Value>=Fields!<xsl:value-of select="$varFieldName"/>.Value</Value>
<CanGrow>true</CanGrow>
<ZIndex>7</ZIndex>
<Style>
<TextAlign>Left</TextAlign>
<PaddingLeft>2pt</PaddingLeft>
<PaddingBottom>2pt</PaddingBottom>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<FontSize><xsl:value-of select="$mvarFontSize"/></FontSize>
<FontWeight><xsl:value-of select="$mvarFontWeight"/></FontWeight>
<BackgroundColor>#e0e0e0</BackgroundColor>
<Color>#000000</Color>
<BorderColor>
<Default>#ffffff</Default>
</BorderColor>
<BorderStyle>
<Default>Solid</Default>
</BorderStyle>
</Style>
</Textbox>
</ReportItems>
</TableCell>
</xsl:template>
<xsl:template match="xs:element" mode="TableColumn">
<TableColumn>
<Width>0.75in</Width>
</TableColumn>
</xsl:template>
<xsl:template name="replace-string">
<xsl:param name="text"/>
<xsl:param name="from"/>
<xsl:param name="to"/>
<xsl:choose>
<xsl:when test="contains($text, $from)">
<xsl:variable name="before" select="substring-before($text, $from)"/>
<xsl:variable name="after" select="substring-after($text, $from)"/>
<xsl:variable name="prefix" select="concat($before, $to)"/>
<xsl:value-of select="$before"/>
<xsl:value-of select="$to"/>
<xsl:call-template name="replace-string">
<xsl:with-param name="text" select="$after"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>