Introduction
I work as software developer in a small, local and independent company. We have a payroll, human resources, and time and attendance for "maquila"
companies in my region (north of Mexico). The environments of work are so diverse, Windows XP, Windows 7, good servers, bad servers, no servers at all (PC's used as servers),
we needed to be able to generate Microsoft Excel files. Our software is developed in C# using SQL Server as database, and we have no problem to generate Excel from reports,
but we needed the ability to generate Excel files from data tables, grids, even from capture screens. At this point things turned very hard because of the diverse working environments.
I did an extensive search for a solution to my specific situation, and I find many options to generate Excel files, but all of them that I tried did't work
in some of the environments that I described.
However, almost all of them involved a more or less direct interface with Excel (typically by adding a reference to your
VS project, instantiating Excel, etc). The lower tech ones don't require that Microsoft Office be installed, since some generate an HTML or XML or CSV file then change the extension
to XLS/XLSX, but they needed that we configure Excel 2010 file blocking settings to allow open files. The same thing happened with a solution that creates an Excel 2.0 file from scratch.
Background
I'm so tired of seeking a fully compatible way to generate Excel files without Excel, and we not have the resources and the logistic to go to every final user PC
to configure Excel 2010, or take the phone calls of our clients to explain how to use the "Save as" option to make files generated by our system writable.
As many of you know, Reporting Services is included with Visual Studio, and is capable of generating reports based on an XML file definition. the only thing that
you need to do is create a report, view in screen and save the report as Excel file.
Please note that this article is not intended to explain how to create a report or using of report viewer, to see that there is a good article
here
Suppose that you has created a report based on "TestTable" table, now you have an rdlc file, that contains XML code like this:
="1.0"="utf-8"
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition"
xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
<DataSources>
<DataSource Name="demoConnectionString">
<rd:DataSourceID>355af724-cf11-4965-afbe-b557985d7a67</rd:DataSourceID>
<ConnectionProperties>
<DataProvider>SQL</DataProvider>
<ConnectString>Data Source=somepc\SQLEXPRESS;Initial Catalog=northwind;Integrated Security=True</ConnectString>
</ConnectionProperties>
</DataSource>
</DataSources>
<InteractiveHeight>11in</InteractiveHeight>
<rd:DrawGrid>true</rd:DrawGrid>
<InteractiveWidth>8.5in</InteractiveWidth>
<rd:SnapToGrid>true</rd:SnapToGrid>
<RightMargin>1in</RightMargin>
<LeftMargin>1in</LeftMargin>
<BottomMargin>1in</BottomMargin>
<rd:ReportID>2c57f813-06b5-4d66-ba3c-30a110b82129</rd:ReportID>
<DataSets>
<DataSet Name="testDataSet_testtable">
<Fields>
<Field Name="column1">
<DataField>COLUMN</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>
<Field Name="column2">
<DataField>COLUMN2</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>
</Fields>
<Query>
<DataSourceName>tesConnectionString</DataSourceName>
<CommandText>SELECT COLUMN1, COLUMMN FROM dbo.TESTTABLE</CommandText>
<rd:UseGenericDesigner>true</rd:UseGenericDesigner>
</Query>
<rd:DataSetInfo>
<rd:DataSetName>testDataSet</rd:DataSetName>
<rd:TableName>TestTable</rd:TableName>
<rd:TableAdapterName>TestTableAdapter</rd:TableAdapterName>
<rd:TableAdapterFillMethod>Fill</rd:TableAdapterFillMethod>
<rd:TableAdapterGetDataMethod>GetData</rd:TableAdapterGetDataMethod>
</rd:DataSetInfo>
</DataSet>
</DataSets>
<Width>6.5in</Width>
<Body>
<ReportItems>
<Table Name="table1">
<DataSetName>testDataSet_testtable</DataSetName>
<Width>4.33334in</Width>
<Details>
<TableRows>
<TableRow>
<TableCells>
<TableCell>
<ReportItems>
<Textbox Name="COLUMN1">
<rd:DefaultName>COLUMN1</rd:DefaultName>
<Style>
<BorderColor>
<Default>Green</Default>
</BorderColor>
<BorderStyle>
<Default>Solid</Default>
</BorderStyle>
<BorderWidth>
<Default>0.5pt</Default>
</BorderWidth>
<TextAlign>Left</TextAlign>
<PaddingLeft>1pt</PaddingLeft>
<PaddingRight>1pt</PaddingRight>
<PaddingTop>1pt</PaddingTop>
<PaddingBottom>1pt</PaddingBottom>
</Style>
<Value>=Fields!COLUMN1.Value</Value>
</Textbox>
</ReportItems>
</TableCell>
<TableCell>
<ReportItems>
<Textbox Name="COLUMN2">
<rd:DefaultName>COLUMN2</rd:DefaultName>
<Style>
<BorderColor>
<Left>Green</Left>
</BorderColor>
<BorderStyle>
<Left>Solid</Left>
</BorderStyle>
<BorderWidth>
<Left>0.5pt</Left>
</BorderWidth>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
<CanGrow>true</CanGrow>
<Value>=Fields!COLUMN2.Value</Value>
</Textbox>
</ReportItems>
</TableCell>
</TableCells>
<Height>0.125in</Height>
</TableRow>
</TableRows>
</Details>
<Header>
<TableRows>
<TableRow>
<TableCells>
<TableCell>
<ReportItems>
<Textbox Name="textbox1">
<rd:DefaultName>textbox1</rd:DefaultName>
<Style>
<Color>White</Color>
<BackgroundColor>DimGray</BackgroundColor>
<BorderColor>
<Bottom>Green</Bottom>
</BorderColor>
<BorderStyle>
<Bottom>Solid</Bottom>
</BorderStyle>
<BorderWidth>
<Bottom>0.5pt</Bottom>
</BorderWidth>
<TextAlign>Right</TextAlign>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
<CanGrow>true</CanGrow>
<Value>COLUMN1</Value>
</Textbox>
</ReportItems>
</TableCell>
<TableCell>
<ReportItems>
<Textbox Name="textbox2">
<rd:DefaultName>textbox2</rd:DefaultName>
<Style>
<PaddingLeft>2pt</PaddingLeft>
<PaddingRight>2pt</PaddingRight>
<PaddingTop>2pt</PaddingTop>
<PaddingBottom>2pt</PaddingBottom>
</Style>
<CanGrow>true</CanGrow>
<Value>COLUMN2</Value>
</Textbox>
</ReportItems>
</TableCell>
</TableCells>
<Height>0.25in</Height>
</TableRow>
</TableRows>
</Header>
<TableColumns>
<TableColumn>
<Width>2.16667in</Width>
</TableColumn>
<TableColumn>
<Width>2.16667in</Width>
</TableColumn>
</TableColumns>
<Height>0.625in</Height>
</Table>
</ReportItems>
<Height>0.75in</Height>
</Body>
<Language>en-US</Language>
<TopMargin>1in</TopMargin>
</Report>
in a form with report viewer control, you assign the file to LocalReport.ReporPath
property and call RefreshReport
method to preview report,
at this point, you can save the report in PDF or Excel formats. But, we cannot design one report for each table, or redesign them when column is added or removed from the table.
Removing connections to SQL
We want to generate report based on DataTable
object, not in SQL Server table, because data table can be populated from server or with results
of memory processes that not exists in the server, to do that change the datasources section on XML file.
<DataSources>
<DataSource Name=ConexionLocal>
<rd:DataSourceID>dsid</rd:DataSourceID>
<ConnectionProperties>
<DataProvider>SQL</DataProvider>
<ConnectString></ConnectString>
</ConnectionProperties>
</DataSource>
</DataSources>
Change Datasets section
Dataset Name property is very important, because this code is designed to work with datables that must vary on columns and names, so you need to give a common name.
<DataSets>
<DataSet Name=dsReporte>
<Fields>
<Field Name="column1">
<DataField>column1</DataField>
<rd:TypeName>System.String32</rd:TypeName>
</Field>
<Field Name="column2">
<DataField>column2</DataField>
<rd:TypeName>System.String32</rd:TypeName>
</Field>
</Fields>
<Query>
<DataSourceName>ConexionLocal</DataSourceName>
<CommandText></CommandText>
<rd:UseGenericDesigner>true</rd:UseGenericDesigner>
</Query>
<rd:DataSetInfo>
<rd:DataSetName>dsReporte</rd:DataSetName>
</rd:DataSetInfo>
</DataSet>
</DataSets>
At this point, our XML file code is
="1.0"="utf-8"
<Report xmlns="http://schemas.microsoft.com/sqlserver/reporting/2005/01/reportdefinition"
xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner">
<DataSources>
<DataSource Name="ConexionLocal">
<rd:DataSourceID>dsid</rd:DataSourceID>
<ConnectionProperties>
<DataProvider>SQL</DataProvider>
<ConnectString></ConnectString>
</ConnectionProperties>
</DataSource>
</DataSources>
<DataSets>
<DataSet Name="dsReporte">
<Fields>
<Field Name="column1">
<DataField>column1</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>
<Field Name="column2">
<DataField>column2</DataField>
<rd:TypeName>System.String</rd:TypeName>
</Field>
</Fields>
<Query>
<DataSourceName>ConexionLocal</DataSourceName>
<CommandText></CommandText>
<rd:UseGenericDesigner>true</rd:UseGenericDesigner>
</Query>
<rd:DataSetInfo>
<rd:DataSetName>dsReporte</rd:DataSetName>
</rd:DataSetInfo>
</DataSet>
</DataSets>
<Width>8.25in</Width>
<InteractiveHeight>11in</InteractiveHeight>
<rd:DrawGrid>true</rd:DrawGrid>
<InteractiveWidth>8.5in</InteractiveWidth>
<rd:SnapToGrid>false</rd:SnapToGrid>
<RightMargin>0.125in</RightMargin>
<LeftMargin>0.125in</LeftMargin>
<BottomMargin>0in</BottomMargin>
<rd:ReportID>c574eb02-0ff6-4305-bfa1-5bfbffb4f42c</rd:ReportID>
<Body>
<ReportItems>
<Table Name="Detalle">
<DataSetName>dsReporte</DataSetName>
<Top>0in</Top>
<Width>12in</Width>
<Details>
<TableRows>
<TableRow>
<TableCells>
<TableCell>
<ReportItems>
<Textbox Name="column1">
<rd:DefaultName>column1</rd:DefaultName>
<Style>
<BorderStyle>
<Default>Solid</Default>
</BorderStyle>
<BorderWidth>
<Default>0.5pt</Default>
</BorderWidth>
<TextAlign>Left</TextAlign>
<PaddingLeft>1pt</PaddingLeft>
<PaddingRight>1pt</PaddingRight>
<PaddingTop>1pt</PaddingTop>
<PaddingBottom>1pt</PaddingBottom>
</Style>
<CanGrow>false</CanGrow>
<Value>=Fields!column1.Value</Value>
</Textbox>
</ReportItems>
</TableCell>
<TableCell>
<ReportItems>
<Textbox Name="column2">
<rd:DefaultName>column2</rd:DefaultName>
<Style>
<BorderStyle>
<Default>Solid</Default>
</BorderStyle>
<BorderWidth>
<Default>0.5pt</Default>
</BorderWidth>
<TextAlign>Left</TextAlign>
<PaddingLeft>1pt</PaddingLeft>
<PaddingRight>1pt</PaddingRight>
<PaddingTop>1pt</PaddingTop>
<PaddingBottom>1pt</PaddingBottom>
</Style>
<CanGrow>false</CanGrow>
<Value>=Fields!column2.Value</Value>
</Textbox>
</ReportItems>
</TableCell>
</TableCells>
<Height>0.1875in</Height>
</TableRow>
</TableRows>
</Details>
<Header>
<TableRows>
<TableRow>
<TableCells>
<TableCell>
<ReportItems>
<Textbox Name="header_column1">
<rd:DefaultName>header_column1</rd:DefaultName>
<Style>
<Color>White</Color>
<BackgroundColor>DimGray</BackgroundColor>
<BorderStyle>
<Default>Solid</Default>
</BorderStyle>
<BorderWidth>
<Default>0.5pt</Default>
</BorderWidth>
<TextAlign>Left</TextAlign>
<PaddingLeft>1pt</PaddingLeft>
<PaddingRight>1pt</PaddingRight>
<PaddingTop>1pt</PaddingTop>
<PaddingBottom>1pt</PaddingBottom>
</Style>
<CanGrow>false</CanGrow>
<Value>column1</Value>
</Textbox>
</ReportItems>
</TableCell>
<TableCell>
<ReportItems>
<Textbox Name="header_column2">
<rd:DefaultName>header_column2</rd:DefaultName>
<Style>
<Color>White</Color>
<BackgroundColor>DimGray</BackgroundColor>
<BorderStyle>
<Default>Solid</Default>
</BorderStyle>
<BorderWidth>
<Default>0.5pt</Default>
</BorderWidth>
<TextAlign>Left</TextAlign>
<PaddingLeft>1pt</PaddingLeft>
<PaddingRight>1pt</PaddingRight>
<PaddingTop>1pt</PaddingTop>
<PaddingBottom>1pt</PaddingBottom>
</Style>
<CanGrow>false</CanGrow>
<Value>column2</Value>
</Textbox>
</ReportItems>
</TableCell>
</TableCells>
<Height>0.1875in</Height>
</TableRow>
</TableRows>
</Header>
<TableColumns>
<TableColumn>
<Width>1in</Width>
</TableColumn>
<TableColumn>
<Width>1in</Width>
</TableColumn>
</TableColumns>
<Height>0.1875in</Height>
</Table>
</ReportItems>
<Height>2in</Height>
</Body>
<Language>en-US</Language>
<TopMargin>0in</TopMargin>
</Report>
Now we have an rdlc file with XML code that we can use as source for report viewer
Create ReportViewer control from code
Remember, I use ReportViewer control to generate Excel file from DataTable
objects, is not intended to preview data on screen (Microsoft.Reporting.WinForms
namespace).
private ReportViewerControl = new ReportViewer();
ReportViewer control uses a ReportDataSource
object as data source, Note that the name assigned in contructor parameter is the same name of DataSet Name property
in DataSets section of the XML file:
private ReportDataSource ReportDataSourceObject = new ReportDataSource("dsReporte");
ReportDataSource object, uses a BindingSource control as his own datasource, create BindingSource control from code:
private BindingSource BindingSourceObject = new BindingSource();
Generating Excel file
First, we need to create and populate DataTable object:
DataTable TestTable=new DataTable();
TestTable.Columns.Add("column1",typeof(string));
TestTable.Columns.Add("column2",typeof(string));
DataRow nRow = null;
nRow=TestTable.NewRow();
nRow["column1"]="row1 column1";
nRow["column2"] = "row1 column2";
nRow.EndEdit();
TestTable.Rows.Add(nRow);
nRow = TestTable.NewRow();
nRow["column1"] = "row2 column1";
nRow["column2"] = "row2 column2";
nRow.EndEdit();
TestTable.Rows.Add(nRow);
Running the example
BindingSourceObject.DataSource = TestTable.DefaultView;
ReportDataSourceObject.Value = BindingSourceObject;
ReportViewerControl.LocalReport.DataSources.Clear();
ReportViewerControl.LocalReport.DataSources.Add(ReportDataSourceObject);
ReportViewerControl.LocalReport.ReportPath = "c:\\report1.rdlc";
Microsoft.Reporting.WinForms.Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
byte[] bytes = ReportViewerControl.LocalReport.Render("Excel", "",
out mimeType, out encoding, out extension, out streamids, out warnings);
FileStream fs = new FileStream("c:\\report1.xls", FileMode.Create);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
Review (functional example)
Now put the code together and make a call properly.
using System;
using System.IO;
using System.Text;
using System.Data;
using Microsoft.Win32;
using System.Data.OleDb;
using System.Collections;
using System.Windows.Forms;
using Microsoft.VisualBasic;
using Microsoft.Reporting.WinForms;
namespace ExcelGen
{
{
private ReportViewer ReportViewerControl = new ReportViewer();
private ReportDataSource ReportDataSourceObject = new ReportDataSource("dsReporte");
private BindingSource BindingSourceObject = new BindingSource();
public void GenerateExcelRS(ref DataTable pTestTable)
{
this.BindingSourceObject.DataSource = pTestTable.DefaultView;
this.ReportDataSourceObject.Value = this.BindingSourceObject;
this.ReportViewerControl.LocalReport.DataSources.Clear();
this.ReportViewerControl.LocalReport.DataSources.Add(this.ReportDataSourceObject);
this.ReportViewerControl.LocalReport.ReportPath = "c:\\report1.rdlc";
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
byte[] bytes = this.ReportViewerControl.LocalReport.Render("Excel", "",
out mimeType, out encoding, out extension, out streamids, out warnings);
FileStream fs = new FileStream("c:\\report1.xls", FileMode.Create);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
}
}
}
To call and generate Excel:
static void Main()
{
DataTable TestTable=new DataTable();
TestTable.Columns.Add("column1",typeof(string));
TestTable.Columns.Add("column2",typeof(string));
DataRow nRow = null;
nRow=TestTable.NewRow();
nRow["column1"]="row1 column1";
nRow["column2"] = "row1 column2";
nRow.EndEdit();
TestTable.Rows.Add(nRow);
nRow = TestTable.NewRow();
nRow["column1"] = "row2 column1";
nRow["column2"] = "row2 column2";
nRow.EndEdit();
TestTable.Rows.Add(nRow);
ExcelGen.ExcelRS ers = new ExcelRS();
ers.GenerateExcelRS(ref TestTable)
}
Not yet
Now we can generate Excel file from data table, but, what if data table changes? Column number and type? Then we need generate the rdlc on the fly,
much better, generate string and pass that string instead of report path to ReportViewerControl.
Generate XML report definition on the fly
Based on rdlc file contents, we can generate a string with the same XML code encoded, here is my solution:
First, add this function to generate a string with XML code inside.
private string GenerateXMLString(ref DataTable pTestTable)
{
string ReturnString = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<Report xmlns=\"http://schemas.microsoft.com/sqlserver/reporting/2005/01/" +
"reportdefinition\" xmlns:rd=\"http://schemas.microsoft.com/" +
"SQLServer/reporting/reportdesigner\">\n" +
" <DataSources>\n" +
" <DataSource Name=\"ConexionLocal\">\n" +
" <rd:DataSourceID>dsid</rd:DataSourceID>\n" +
" <ConnectionProperties>\n" +
" <DataProvider>SQL</DataProvider>\n" +
" <ConnectString></ConnectString>\n" +
" </ConnectionProperties>\n" +
" </DataSource>\n" +
" </DataSources>\n" +
" <DataSets>\n" +
" <DataSet Name=\"dsReporte\">\n" +
" <Fields>\n";
foreach (DataColumn lColumn in pTestTable.Columns)
{
ReturnString += " <Field Name=\"" + lColumn.ColumnName + "\">\n" +
" <DataField>" + lColumn.ColumnName + "</DataField>\n" +
" <rd:TypeName>" + lColumn.DataType.ToString() + "</rd:TypeName>\n" +
" </Field>\n";
}
ReturnString += " </Fields>\n" +
" <Query>\n" +
" <DataSourceName>ConexionLocal</DataSourceName>\n" +
" <CommandText></CommandText>\n" +
" <rd:UseGenericDesigner>true</rd:UseGenericDesigner>\n" +
" </Query>\n" +
" <rd:DataSetInfo>\n" +
" <rd:DataSetName>dsReporte</rd:DataSetName>\n" +
" </rd:DataSetInfo>\n" +
" </DataSet>\n" +
" </DataSets>\n" +
" <Width>8.25in</Width>\n" +
" <InteractiveHeight>11in</InteractiveHeight>\n" +
" <rd:DrawGrid>true</rd:DrawGrid>\n" +
" <InteractiveWidth>8.5in</InteractiveWidth>\n" +
" <rd:SnapToGrid>false</rd:SnapToGrid>\n" +
" <RightMargin>0.125in</RightMargin>\n" +
" <LeftMargin>0.125in</LeftMargin>\n" +
" <BottomMargin>0in</BottomMargin>\n" +
" <rd:ReportID>c574eb02-0ff6-4305-bfa1-5bfbffb4f42c</rd:ReportID>\n" +
" <Body>\n" +
" <ReportItems>\n" +
" <Table Name=\"Detalle\">\n" +
" <DataSetName>dsReporte</DataSetName>\n" +
" <Top>0in</Top>\n" +
" <Width>12in</Width>\n" +
" <Details>\n" +
" <TableRows>\n" +
" <TableRow>\n" +
" <TableCells>\n";
foreach (DataColumn lColumn in pTestTable.Columns)
{
ReturnString += " <TableCell>\n" +
" <ReportItems>\n" +
" <Textbox Name=\"" + lColumn.ColumnName + "\">" +
" <rd:DefaultName>" + lColumn.ColumnName + "</rd:DefaultName>\n" +
" <Style>\n" +
" <BorderStyle><Default>Solid</Default></BorderStyle>\n" +
" <BorderWidth><Default>0.5pt</Default></BorderWidth>\n" +
" <TextAlign>Left</TextAlign>\n" +
" <PaddingLeft>1pt</PaddingLeft>\n" +
" <PaddingRight>1pt</PaddingRight>\n" +
" <PaddingTop>1pt</PaddingTop>\n" +
" <PaddingBottom>1pt</PaddingBottom>\n" +
" </Style>\n" +
" <CanGrow>false</CanGrow>\n" +
" <Value>=Fields!" + lColumn.ColumnName + ".Value</Value>\n" +
" </Textbox>\n" +
" </ReportItems>\n" +
" </TableCell>\n";
}
ReturnString += "</TableCells>\n" +
" <Height>0.1875in</Height>\n" +
" </TableRow>\n" +
" </TableRows>\n" +
" </Details>\n" +
" <Header>\n" +
" <TableRows>\n" +
" <TableRow>\n" +
" <TableCells>\n";
foreach (DataColumn lColumn in pTestTable.Columns)
{
ReturnString += " <TableCell>\n" +
" <ReportItems>\n" +
" <Textbox Name=\"header_" + lColumn.ColumnName + "\">\n" +
" <rd:DefaultName>header_" + lColumn.ColumnName + "</rd:DefaultName>\n" +
" <Style>\n" +
" <Color>White</Color>\n" +
" <BackgroundColor>DimGray</BackgroundColor>\n" +
" <BorderStyle><Default>Solid</Default></BorderStyle>\n" +
" <BorderWidth><Default>0.5pt</Default></BorderWidth>\n" +
" <TextAlign>Left</TextAlign>\n" +
" <PaddingLeft>1pt</PaddingLeft>\n" +
" <PaddingRight>1pt</PaddingRight>\n" +
" <PaddingTop>1pt</PaddingTop>\n" +
" <PaddingBottom>1pt</PaddingBottom>\n" +
" </Style>\n" +
" <CanGrow>false</CanGrow>\n" +
" <Value>" + lColumn.ColumnName + "</Value>\n" +
" </Textbox>\n" +
" </ReportItems>\n" +
" </TableCell>\n";
}
ReturnString += "</TableCells>\n" +
" <Height>0.1875in</Height>\n" +
" </TableRow>\n" +
" </TableRows>\n" +
" </Header>\n" +
" <TableColumns>\n";
foreach (DataColumn lColumn in pTestTable.Columns)
{
ReturnString += " <TableColumn>\n" +
" <Width>1in</Width>\n" +
" </TableColumn>\n";
}
ReturnString += " </TableColumns>\n" +
" <Height>0.1875in</Height>\n" +
" </Table>\n" +
" </ReportItems>\n" +
" <Height>2in</Height>\n" +
" </Body>\n" +
" <Language>en-US</Language>\n" +
" <TopMargin>0in</TopMargin>\n" +
"</Report>";
return ReturnString;
}
Now that we have the string with the report definition, we need to convert that string in bytes stream, encoded on UTF-8 and convert those bytes to Memory stream.
Add this function to the ExcelRS
class:
private MemoryStream EncodeString(ref DataTable pTestTable)
{
string strDef = GenerateXMLString(ref pTestTable);
byte[] strbytes = Encoding.UTF8.GetBytes(strDef);
return new System.IO.MemoryStream(strbytes);
}
and now we are capable to generate Excel files for any data table, replace this function on ExcelRS
class:
public void GenerateExcelRS(ref DataTable pTestTable)
{
MemoryStream lStream = EncodeString(ref pTestTable);
this.BindingSourceObject.DataSource = pTestTable.DefaultView;
this.ReportDataSourceObject.Value = this.BindingSourceObject;
this.ReportViewerControl.LocalReport.DataSources.Clear();
this.ReportViewerControl.LocalReport.DataSources.Add(this.ReportDataSourceObject);
this.ReportViewerControl.LocalReport.LoadReportDefinition(lStream);
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
byte[] bytes = this.ReportViewerControl.LocalReport.Render("Excel",
"", out mimeType, out encoding, out extension, out streamids, out warnings);
FileStream fs = new FileStream("c:\\report1.xls", FileMode.Create);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
}
Dynamic reporting
As you can see, if you change the word "Excel" on render method to "PDF", you can generate PDF files on the fly too, and as you noted, you can
generate dynamic reports modifying this implementation. good luck and enjoy!