Table of Contents | ||||
---|---|---|---|---|
|
Introduction to ExcelApplication API
Info |
---|
The sample code template (template.xlsx), page (Part1_CSV.aspx), and code behind (Part1_CSV.aspx.cs) are included in the ExtendedSalesSummary project available for download as part of the ExcelWriter Basics Tutorials. |
This tutorial assumes that the coder knows how to set up an Excel Template using data markers and Excel cell formatting. The .XLSX template file is available here.
ExcelTemplate vs. ExcelApplication
Info |
---|
ExcelTemplate is the main class for template-driven document generation. This object opens an ExcelWriter template file, populates it with data from a specified data source, and generates a new Excel workbook. An Excel file uploaded as an |
This tutorial opens an Excel file formatted as an ExcelTemplate origin file (i.e. it contains formatted data markers) using ExcelApplication
object. This means that the file can be copied based upon the user's specification using the ExcelApplication CopySheet method. This method copies a worksheet (including its formatted data markers) to another location in the workbook. The method has three parameters:
- A Worksheet object representing the sheet to be copied,
- An integer representing the new worksheet's position and,
- The string representing the name of the new worksheet.
Setting up the template
The template file should look something like this:
One advanced data marker format has been used in the above template. The data marker entitled Sales Summary - %%=Header.FiscalYear references another cell for the content. The below image is the data marker that was input and is how ExcelWriter will identify the appropriate fields to bind data.
Cell N1 was hidden by using a custom format where the field was filled with ';;;'.
Info |
---|
In the sample code, the complete template file is located in ExtendedSalesSummary/templates/template.xlsx .The template is also available here. |
Adding an ExcelWriter Reference in Visual Studio
Info |
---|
In the sample code, the reference to SoftArtisans.OfficeWriter.ExcelWriter.dll and to System.Data.SqlClient has already been added to the ExtendedSalesSummary project. |
Create a .NET project and add a reference to the ExcelWriter library.
- Open Visual Studio and create a .NET project.
- The sample code uses a web application.
- Add a reference to SoftArtisans.OfficeWriter.ExcelWriter.dll
- SoftArtisans.OfficeWriter.ExcelWriter.dll is located under Program Files > SoftArtisans > OfficeWriter > dotnet > bin
Writing the Code
The code for this report uses two functions- the Main
function and the BindCountryData
functions. The Main
function calls the BindCountryData
function taking each country's worksheet as a parameter. See below for further explanation.
Main function
1. Include the SoftArtisans.OfficeWriter.ExcelWriter namespace in the code behind
Code Block | ||
---|---|---|
| ||
using SoftArtisans.Office.ExcelWriter; |
Info | ||
---|---|---|
If you are following along and intend to use AdventureWorks2008R2, you will also want to make a reference to
|
2. In the method that will run the report, instantiate the ExcelApplication object.
Code Block | ||
---|---|---|
| ||
ExcelApplication xla = new ExcelApplication(); |
3. Open the ExcelApplication
object with the ExcelApplication.Open() method and instantiate it as a Workbook object.
Code Block |
---|
Workbook wb = xla.Open(Page.MapPath(@"templates\template.xlsx")); |
4. Assuming that you taking the selections from a ASP.NET ListBox, use the following utility method to create a string list of all the countries that were selected.
Code Block |
---|
protected List<string> GetListBoxSelections(){ //Get the ListBox selections and make the appropriate number of copies List<string> countryNames = new List<string>(); foreach (int i in ListBox1.GetSelectedIndices()) { countryNames.Add(ListBox1.Items[i].Text); } return countryNames; } |
Info |
---|
A utility method refers to a piece of code/function that is not affected by the ExcelWriter API framework. For the purpose of this tutorial, utility methods will be included as a separate function outside of the Main function. |
5. ExcelApplication can make copies of a specified worksheet using the Worksheets.CopySheet() method. This method requires a Worksheet object representing the sheet to be copied; an integer representing the new worksheet's position and, the string representing the name of the new worksheet.
Insert the following code that iterates through the selected countries in the string list to make copies of the "SimpleTemplate" in the original template.xlsx file. Each new worksheet is placed after the "SimpleTemplate" sheet and is named based on the selected country from the string list. The <i>for<i> loop uses the <i>List<i>.Count method to determine how many copies must be made.
Code Block |
---|
for (int i = 0; i < selectedCountries.Count; i++) { wb.Worksheets.CopySheet(wb.Worksheets[0], wb.Worksheets.Count, selectedCountries[i]); } |
6. At this point, there is a worksheet named after each selected country and the original worksheet contained in the wb
Workbook object. We need to hide the original worksheet using the Worksheet.Visibility method. This method can also make a worksheet 'visible' or 'very hidden.'
Code Block |
---|
wb.Worksheets[0].Visibility = Worksheet.SheetVisibility.Hidden; |
7. Select the first worksheet after the now hidden one to be displayed when the file first is opened using the Worksheets.Select() method.
Code Block |
---|
wb.Worksheets[1].Select(); |
8. Instantiate a new ExcelTemplate
object.
Code Block |
---|
ExcelTemplate xlt = new ExcelTemplate(); |
9. Open the ExcelApplication
workbook using the ExcelTemplate.Open(ExcelApplication, Workbook) method.
Code Block |
---|
xlt.Open(xla, wb); |
10. The use of function in an ExcelWriter code file is sometimes the most efficient way to approach a situation. In this case, a separate function is created to bind the data. This function is called in a for loop that is contained with the other code. Let the function that deals with data binding be called BindCountryData()
and takes a worksheets name's as a string parameter.
Code Block |
---|
for (int i = 0; i < wb.Worksheets.Count; i++) { BindCountryData(wb.Worksheets[i].Name); } |
Info |
---|
We will return to this function after completing the main code. |
11. Call ExcelTemplate.Process() to import all data into the file.
Code Block |
---|
xlt.Process(); |
12. Call ExcelTemplate.Save to save the output file.
ExcelTemplate has several output options: save to disk, save to a stream, stream the output file in a page's Response inline or as an attachment.
Code Block |
---|
xlt.Save(Page.Response, "output.xlsx", false); |
BindCountryData function
1. Create a function called BindCountryData
that takes a string parameter called selection
.
Code Block |
---|
protected void BindCountryData(string selection){} |
2. Create a DataBindingProperty based on the Worksheet name. DataBindingProperties
is like a tag used to specify how data is bound to a worksheet.
Other DataBindingProperties
include MaxRows and Transpose.
Code Block |
---|
DataBindingProperties dbp = xlt.CreateDataBindingProperties(); dbp.WorksheetName = selection; |
3. Create an string array for the header values and a string array for the column names.
ExcelTemplate
can be bound to numerous types of .NET data structures: single variables, arrays (1-D, jagged, multi-dimensional), DataSet, DataTable, IDataReader
etc. The source of the data can come from anywhere.
Some of the aforementioned structures have built in column names, such as the DataTable. When working with arrays, which don't have built in column names, you have to define the column names in a separate string array.
Code Block |
---|
string[] specificInfo = { "FY 2008", "Foreign Trade Division", selection }; string[] headerTitles = { "FiscalYear", "TradeDivision", "Country" }; |
4. Use the ExcelTemplate.BindRowData method to bind the header data to the data markers in the template file (%%=Header.FiscalYear, %%=Header.TradeDivision, %%=Header.Country).
BindRowData() binds a single row of data to the template, but the data markers in the template do not need to be in a single row.
Code Block |
---|
xlt.BindRowData(specificInfo, headerTitles, "Header", dbp); |
5. Get the data for the Top 5 Expenses and All Expenses data sets.
Code Block |
---|
DataTable dts = Sales(selection, "TOP 5"); DataTable dts2 = Sales(selection, ""); |
In this tutorial, it is assumed that your machine is equipped with AdventureWorks2008R2, and therefore that a SQL query is a valid operation. As a utility method, the following function will be included as a function outside of the Main and BindCountryData functions.
Code Block |
---|
public static DataTable Sales(string selection, string topORall) { //Create the query string queryString = "USE AdventureWorks2008R2; " + "DECLARE @find nvarchar(100); " + "SET @find ='" + selection + "'; " + "SELECT " + topORall + " Sales.Store.Name AS Description, Sales.SalesOrderHeader.TotalDue AS Sales FROM Sales.Store " + "INNER JOIN Sales.Customer ON Sales.Store.BusinessEntityID = Sales.Customer.StoreID " + "INNER JOIN Sales.SalesOrderHeader ON Sales.Customer.CustomerID = Sales.SalesOrderHeader.CustomerID " + "INNER JOIN Sales.SalesTerritory ON Sales.SalesTerritory.TerritoryID = Sales.Customer.TerritoryID " + "WHERE DATEPART(yyyy, Sales.SalesOrderHeader.OrderDate) = 2008 " + "AND Sales.SalesOrderHeader.OnlineOrderFlag = 0 " + "AND Sales.SalesTerritory.Name = @find " + "ORDER BY Sales DESC;"; //Get connection SqlConnection connection = new SqlConnection(@"Data Source=TS-IH03\SQL2008R2;Initial Catalog=ExcelApp2;Integrated Security=True"); //Assign command and associated connection SqlCommand cmd = new SqlCommand(queryString, connection); //Select data adapter SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = cmd; //Make a new table, fill it and return it DataTable dtGet = new DataTable(); adapter.Fill(dtGet); return dtGet; } |
Info |
---|
If you do not have AdventureWorks2008R2, you may download the sample file here. The sample code uses a utility method called GetCSVData to obtain data from the supplied CSV files. |
6. Use ExcelTemplate.BindData to bind the data for the Top and Details Sales data sets.
Recall that the data source names (Top, Details) need to match the data marker names exactly.
Code Block |
---|
xlt.BindData(dts, "Top", dbp); xlt.BindData(dts2, "Details", dbp); |
7. Now you may run your code.
Here is an example of what the sample will look like:
Note that there are multiple worksheets, each named for each country and containing it's specified data.
Final Code
Code Block |
---|
using SoftArtisans.Office.ExcelWriter; using System.Data.SqlClient; using System.Collections.Generic; ... protected void Main(object sender, EventArgs e){ ExcelApplication xla = new ExcelApplication(); Workbook wb = xla.Open(Page.MapPath(@"templates\template.xlsx")); for (int i = 0; i < selectedCountries.Count; i++) { wb.Worksheets.CopySheet(wb.Worksheets[0], wb.Worksheets.Count, selectedCountries[i]); } wb.Worksheets[0].Visibility = Worksheet.SheetVisibility.Hidden; wb.Worksheets[1].Select(); xlt = new ExcelTemplate(); xlt.Open(xla, wb); for (int i = 0; i < wb.Worksheets.Count; i++) { BindCountryData(wb.Worksheets[i].Name); } xlt.Process(); xlt.Save(Page.Response, "output.xlsx", false); } protected void BindCountryData(string selection) { DataBindingProperties dbp = xlt.CreateDataBindingProperties(); dbp.WorksheetName = selection; string[] specificInfo = { "FY 2008", "Foreign Trade Division", selection }; string[] headerTitles = { "FiscalYear", "TradeDivision", "Country" }; xlt.BindRowData(specificInfo, headerTitles, "Header", dbp); DataTable dts = Sales(selection, "TOP 5"); DataTable dts2 = Sales(selection, ""); xlt.BindData(dts, "Top", dbp); xlt.BindData(dts2, "Details", dbp); } public static DataTable Sales(string selection, string topORall) { //Create the query string queryString = "USE AdventureWorks2008R2; " + "DECLARE @find nvarchar(100); " + "SET @find ='" + selection + "'; " + "SELECT " + topORall + " Sales.Store.Name AS Description, Sales.SalesOrderHeader.TotalDue AS Sales FROM Sales.Store " + "INNER JOIN Sales.Customer ON Sales.Store.BusinessEntityID = Sales.Customer.StoreID " + "INNER JOIN Sales.SalesOrderHeader ON Sales.Customer.CustomerID = Sales.SalesOrderHeader.CustomerID " + "INNER JOIN Sales.SalesTerritory ON Sales.SalesTerritory.TerritoryID = Sales.Customer.TerritoryID " + "WHERE DATEPART(yyyy, Sales.SalesOrderHeader.OrderDate) = 2008 " + "AND Sales.SalesOrderHeader.OnlineOrderFlag = 0 " + "AND Sales.SalesTerritory.Name = @find " + "ORDER BY Sales DESC;"; //Get connection SqlConnection connection = new SqlConnection(@"Data Source=TS-IH03\SQL2008R2;Initial Catalog=ExcelApp2;Integrated Security=True"); //Assign command and associated connection SqlCommand cmd = new SqlCommand(queryString, connection); //Select data adapter SqlDataAdapter adapter = new SqlDataAdapter(); adapter.SelectCommand = cmd; //Make a new table, fill it and return it DataTable dtGet = new DataTable(); adapter.Fill(dtGet); return dtGet; } protected List<string> GetListBoxSelections(){ //Get the ListBox selections and make the appropriate number of copies List<string> countryNames = new List<string>(); foreach (int i in ListBox1.GetSelectedIndices()) { countryNames.Add(ListBox1.Items[i].Text); } return countryNames; } |
Downloads
You can download the code for the Extended Sales Summary here.