Showing posts with label Excel. Show all posts
Showing posts with label Excel. Show all posts

Tuesday, 2 April 2013

Exporting to Excel from GridView when grid changed by code behind

This is a small post, which explains how to export to excel from GridView where the GridView has been changed from the code behind by adding/deleting some rows and updated some data. When GridView changed from code behind, the export might not include the updated version done from the code behind.

Understanding a sample scenario:

Before going for implementation, let us understand the scenario where this implementation is applicable. I have a GridView, which binds some sample data from an XML file. The GridView on screen will be as below –

Now I want to change same GridView by adding group total and grand total as per the post Group Total and Grand Total in GridView - Part 2. Now my GridView shows as below –

Here, the group total, grand total rows are added from code behind by doing some logic. I want to export the same GridView to Excel. I followed normal way of export to excel as per the post Exporting to Excel from GridView (All columns and rows - Normal Method). But I am not getting all the rows exported to the Excel, and the total values are not getting exported. The excel shows as below –

I am not sure why this happened. But seems it exported only the number of rows as per the data it binds. Also RowDataBound event also not fired while exporting, so excel not updated with the total value.

Solution –

The implementation is very simple. The actual requirement while exporting to excel is, we required the html script which needs to be exported to excel. In a normal way, we render the GridView at the time of export (again) and get the html script. The html script is then used for exporting to excel. The code is below –
PrepareGridViewForExport(grdViewOrders);

Context.Response.ClearContent();
Context.Response.ContentType = "application/ms-excel";
Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "ExcelFileName"));
Context.Response.Charset = "";
System.IO.StringWriter stringwriter = new System.IO.StringWriter();
HtmlTextWriter htmlwriter = new HtmlTextWriter(stringwriter);
grdViewOrders.RenderControl(htmlwriter);
Context.Response.Write(stringwriter.ToString());
Context.Response.End();
But instead of doing this, we can use the actual html script which already been rendered on the screen. So to export the actual html on screen to excel, I added a div as parent to GridView. I also added a hidden control for storing the html script before going to code behind. In the export button, I called a javascript function which gets the html script of GridView and assign to the hidden control (before going to code behind).

function AssignExportHTML() {
    document.getElementById("<%= hidGridView.ClientID %>").value = htmlEscape(forExport.innerHTML);
}
function htmlEscape(str) {
    return String(str)
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}
In the code behind, I used the hidden control value for exporting to the grid.
Context.Response.ClearContent();
Context.Response.ContentType = "application/ms-excel";
Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "ExcelFileName"));
Context.Response.Charset = "";
System.IO.StringWriter stringwriter = new System.IO.StringWriter();
//HtmlTextWriter htmlwriter = new HtmlTextWriter(stringwriter);
//grdViewOrders.RenderControl(htmlwriter);
stringwriter.Write(System.Web.HttpUtility.HtmlDecode(hidGridView.Value));
Context.Response.Write(stringwriter.ToString());
Context.Response.End();
Now my export will be as below –

To get the same style and colors used in the GridView, we can add the css style sheet inside the div control. So while exporting, the style sheets also goes to the excel sheet.

Now the excel looks as below –

Note: This implementation can be used for exporting any kind grid, tables etc to excel as shown in the screen.

Tuesday, 25 October 2011

Exporting to Excel using Excel Application object



I already blogged some post on Exporting to Excel using Grid View previously. On the same series, this post concentrates on Exporting to excel using Microsoft.Office.Interop.Excel namespace.

For exporting functionality, we are going to use ApplicationClass class defined under Microsoft.Office.Interop.Excel namespace. So to implement this concept, we must add Microsoft.Office.Interop.Excel assembly reference in the project.

Note:
  • This implementation can be used in both ASP.NET and Windows Applications as it does not required Grid View or any ASP.NET controls.
  • As the code using Excel objects, it required Excel Application installed in the server (where the code runs). It means, if the application is an ASP.NET application - the Web Server must have Excel 2007 software installed or if the application is a Win32, WPF etc., applications then the client system must have Excel 2007 installed).
  • I have Office 2007 installed in my system, so I reference the Microsoft.Office.Interop.Excel DLL version 12.0.0.0. If you have other Office version, please refer the respective DLL version.
  • I use Northwind database for getting the data to the excel sheet. So to use the working code, make sure you have Northwind database in the SQL Server and change the connection string.

The requirement in this implementation is to have a button (btnExport) control on Web Page. When the user clicks the button, the system should fetch the order details and export an Excel sheet. The system should not depend on any ASP.NET control (like Grid View) for exporting the data and it should use Excel ApplicationClass object.

To achieve this requirement I have done the following steps -

Step 1: Defining ExcelAppExporter class

I have a reusable class ExcelAppExporter in the project. This is a generic class which accepts an IList collection and list of exporting column names with its properties as input.

Below is the code for ExcelAppExporter class (look at the ExcelAppExportor.cs file in the source code - Sorry for spell mistake, I will change it soon.)
/// <summary>
/// Class to Export the Data to Excel sheet. Input must be a IList colection
/// </summary>
/// <typeparam name="T">Entity</typeparam>
public class ExcelAppExporter<T>
{
    /// <summary>
    /// Constructor
    /// </summary>
    public ExcelAppExporter()
    {
        TemplateFileName = string.Empty;
        IsExportIncludesHeader = true;
        ExportSheetName = "Export";
    }
    /// <summary>
    /// Holds the Data
    /// </summary>
    private IList<T> ListData;

    /// <summary>
    /// Holds the Export Columns
    /// </summary>
    private IList<ExportColumn> ExportColumns;

    /// <summary>
    /// Add the export column
    /// </summary>
    /// <param name="ExportColumn">ExportColumn</param>
    public void AddExportColumn(ExportColumn ExportColumn)
    {
        if (ExportColumns == null) ExportColumns = new List<ExportColumn>();
        ExportColumns.Add(ExportColumn);
    }
    /// <summary>
    /// List contains the list of entity object which are export to excel
    /// </summary>
    public IList<T> BindDataList
    {
        get { return ListData; }
        set { ListData = value; }
    }
    /// <summary>
    /// File Name of the Export output file
    /// </summary>
    public string ExportFileName { get; set; }

    /// <summary>
    /// Template File Name - Using Template file to Export
    /// </summary>
    public string TemplateFileName { get; set; }

    /// <summary>
    /// Sheet name to Export the data
    /// </summary>
    public string ExportSheetName { get; set; }

    /// <summary>
    /// Is the header data needs to be exported
    /// </summary>
    public bool IsExportIncludesHeader { get; set; }

    public void Export()
    {
        try
        {
            #region Filling Export Columns
            // Check the columns to export is mentioned, if not
            if (ExportColumns == null)
            {
                // Create an entity object. If the list count == 0 ??? - needs to be handled from client
                T tEntity = ListData[0];

                // Export the columns to export from the property name
                ExportColumns = new List<ExportColumn>();
                foreach (System.Reflection.PropertyInfo propertyInfo in tEntity.GetType().GetProperties())
                    ExportColumns.Add(new ExportColumn(propertyInfo.Name, propertyInfo.Name));
            }
            #endregion

            // Create excel application
            Application ExcelApp = new ApplicationClass();
            Workbook workbook;
            Sheets sheets;
            Worksheet worksheet;

            // Is Export needs to be exported to a Template file
            if (TemplateFileName != string.Empty)
            {
                #region Load the Template file
                // Load the work book
                workbook = ExcelApp.Workbooks.Open(TemplateFileName, 0, false, 5, "", "", false,
                    XlPlatform.xlWindows, "", true, false, 0, true, false, false);

                sheets = workbook.Sheets;
                worksheet = (Worksheet)sheets.get_Item(1); // To avoid unassigned variable error

                bool IsWorkSheetFound = false;

                //Check is there any worksheet with the name provided. If yes, clear all data inside to fill new data
                for (int intSheetIndex = 1; intSheetIndex <= sheets.Count; intSheetIndex++)
                {
                    worksheet = (Worksheet)sheets.get_Item(intSheetIndex);
                    if (worksheet.Name.ToString().Equals(ExportSheetName))
                    {
                        IsWorkSheetFound = true;
                        break;
                    }
                }

                // If No work sheet found, add it at the last
                if (!IsWorkSheetFound)
                {
                    worksheet = (Worksheet)workbook.Sheets.Add(
                        Type.Missing, (Worksheet)sheets.get_Item(sheets.Count),
                        Type.Missing, Type.Missing);
                    worksheet.Name = ExportSheetName;
                }
                #endregion
            }
            else
            {
                #region Crate the Template File
                // Adding new work book
                workbook = ExcelApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);

                sheets = workbook.Sheets;

                worksheet = (Worksheet)sheets.get_Item(1);

                worksheet.Name = ExportSheetName;
                #endregion
            }
            int intCol = 0;

            #region Populating the Header
            // If the header needs to exported. In templated files, normally we will have the headings
            if (IsExportIncludesHeader == true)
            {
                // Exporting Header
                foreach (ExportColumn exportColumn in ExportColumns)
                {
                    Range range = (Range)worksheet.Cells[exportColumn.StartRowIndex++, ((exportColumn.ExcelColumnIndex == 0) ? ++intCol : exportColumn.ExcelColumnIndex - 1)];
                    range.Select();
                    range.Value2 = exportColumn.HeaderText.ToString();
                    range.Columns.EntireColumn.AutoFit();
                    range.Font.Bold = true;
                }
            }
            #endregion

            // Exporting Data 
            foreach (T tEntity in BindDataList)
            {
                intCol = 0;
                foreach (ExportColumn exportColumn in ExportColumns)
                {
                    Range range = (Range)worksheet.Cells[exportColumn.StartRowIndex++, ((exportColumn.ExcelColumnIndex == 0) ? ++intCol : exportColumn.ExcelColumnIndex)];
                    range.Select();
                    range.Value2 = tEntity.GetType().GetProperty(exportColumn.ColumnName.ToString()).GetValue(tEntity, null).ToString();
                    range.Columns.EntireColumn.AutoFit();
                }
            }
            intCol = 0;
            foreach (ExportColumn exportColumn in ExportColumns)
            {
                Range range = (Range)worksheet.Cells[exportColumn.StartRowIndex++, ((exportColumn.ExcelColumnIndex == 0) ? ++intCol : exportColumn.ExcelColumnIndex)];
                if (exportColumn.ValueFormat != string.Empty)
                    range.Columns.EntireColumn.NumberFormat = exportColumn.ValueFormat;
            }
            try
            {
                // Save the workbook with saving option
                workbook.Close(true, ExportFileName, Type.Missing);
                ExcelApp.UserControl = false;
                ExcelApp.Quit();
                ExcelApp = null;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            Process[] ps = Process.GetProcesses();
            foreach (Process p in ps)
            {
                if (p.ProcessName.ToLower().Equals("excel"))
                    p.Kill();
            }
        }
    }
}
As defined in the code, the class required two types of inputs.

  1. An IList collection, which contains list of entity objects. The type of the entity object will be defined while creating the ExcelAppExporter object.
  2. An entity object from the IList collection may contain many attributes, but we may required to export only some of the attributes (here attributes defines the columns and entity objects defines rows in excel sheet). So to define the list of ExportColumn for exporting to excel from the entity object, we have a class ExportColumn.
    So, to let the system understand what are the columns needs to be exported to excel sheet - the column name (attribute name) must be provided as input to the ExcelAppExporter. To define the export column details, we have another entity class ExportColumn which is used for adding the column using AddExportColumn method defined with ExcelAppExporter.

The ExportColumn class is defined below:
/// <summary>
/// Holds the details of te columns to be exported to Excel from IList
/// </summary>
public class ExportColumn
{
    /// <summary>
    /// Entity object(class) property name, mandatory
    /// </summary>
    public string ColumnName { get; set; }

    /// <summary>
    /// What needs to be shows in the Excel sheet for that col, mandatory
    /// </summary>
    public string HeaderText { get; set; }

    /// <summary>
    /// The format of the value. To get the format string, from the excel sheet -> Column -> Format Cells -> Number -> Custom -> Take the Type
    /// </summary>
    public string ValueFormat { get; set; }

    /// <summary>
    /// From which row the data needs to be exported, for template file it will be useful
    /// </summary>
    public int StartRowIndex { get; set; }

    /// <summary>
    /// On which column the data needs to be exports, So it can be any column. Useful for Templated file
    /// Needs to assign the Index of the column starting by 1
    /// A - 1, B - 2, C - 3 etc., Counting will include the hidden column
    /// </summary>
    public int ExcelColumnIndex { get; set; }

    public ExportColumn()
    {
        StartRowIndex = 1;
        this.ValueFormat = string.Empty;
        this.StartRowIndex = 1;
        this.ExcelColumnIndex = 0;
    }
    public ExportColumn(string ColumnName, string HeaderText)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = string.Empty;
        this.StartRowIndex = 1;
        this.ExcelColumnIndex = 0;
    }
    public ExportColumn(string ColumnName, string HeaderText, int StartRowIndex, int ExcelColumnIndex)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = string.Empty;
        this.StartRowIndex = StartRowIndex;
        this.ExcelColumnIndex = ExcelColumnIndex;
    }
    public ExportColumn(string ColumnName, string HeaderText, string ValueFormat)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = ValueFormat;
        this.StartRowIndex = 1;
        this.ExcelColumnIndex = 0;
    }
    public ExportColumn(string ColumnName, string HeaderText, string ValueFormat, int StartRowIndex, int ExcelColumnIndex)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = ValueFormat;
        this.StartRowIndex = StartRowIndex;
        this.ExcelColumnIndex = ExcelColumnIndex;
    }
}

As already discussed, the ExportColumn class is used for adding the exporting column name, header text and format of the data. The format of the data should as per the format defined in excel sheet (Select the column in Excel Sheet -> right click the column -> select Format Cells... -> Define any format -> Select the Custom -> tab -> Copy the Type value).
  1. ExportColumn(string ColumnName, string HeaderText) - Defining column name of the database table/view/query and the column header text on the Excel Sheet.
  2. ExportColumn(string ColumnName, string HeaderText, int StartRowIndex, int ExcelColumnIndex) - Defining column name, column header text, row index where the data should start to export, column index where the data should start to export.
  3. ExportColumn(string ColumnName, string HeaderText, string ValueFormat) - Defining column name, column header text, value format of the data.
  4. ExportColumn(string ColumnName, string HeaderText, string ValueFormat, int StartRowIndex, int ExcelColumnIndex) - Defining column name, column header text, value format of the data, row index where the data should start to export, column index where the data should start to export.

The btnExport button event in the code behind will create object of ExcelAppExporter class and pass the records as IList collection. Below code shows how it works.

/// <summary>
/// No Template file is used
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnExport_Click(object sender, EventArgs e)
{
    ExcelAppExportor<Order> exportor = new ExcelAppExportor<Order>();
    exportor.BindDataList = OrderList();
    exportor.AddExportColumn(new ExportColumn("OrderID", "Order ID"));
    exportor.AddExportColumn(new ExportColumn("CustomerID", "Customer ID"));
    exportor.AddExportColumn(new ExportColumn("CustomerName", "Customer Name"));
    exportor.AddExportColumn(new ExportColumn("OrderDate", "Order Date", "dd/mmm/yyyy"));
    exportor.AddExportColumn(new ExportColumn("UnitPrice", "Unit Price", "[$$-409]#,##0.00_);([$$-409]#,##0.00)"));
    exportor.AddExportColumn(new ExportColumn("Quantity", "Quantity", "##0"));
    exportor.AddExportColumn(new ExportColumn("Discount", "Discount", "[$$-409]#,##0.00_);([$$-409]#,##0.00)"));
    exportor.AddExportColumn(new ExportColumn("TotalAmount", "Total Amount", "[$$-409]#,##0.00_);([$$-409]#,##0.00)"));

    string strExportFileName = Path.Combine(@"C:\Temp", Guid.NewGuid().ToString("N")) + ".xls";
    exportor.ExportFileName = strExportFileName;
    exportor.ExportSheetName = "Orders";
    exportor.Export();

    byte[] ExcelStream = File.ReadAllBytes(strExportFileName);

    Context.Response.ClearContent();
    Context.Response.ContentType = "application/ms-excel";
    Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "Product List"));
    Context.Response.Charset = "";
    Context.Response.BinaryWrite(ExcelStream);
    Context.Response.End();
}
/// <summary>
/// Method which binds the data to the Grid
/// </summary>
private IList<Order> OrderList()
{
    using (SqlConnection connection =
        new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString))
    {

        SqlCommand command = new SqlCommand(
                "Select Top 20 Orders.OrderID, Orders.CustomerID, Suppliers.CompanyName, Orders.OrderDate, " +
                "OrderDetails.UnitPrice, OrderDetails.Quantity, OrderDetails.Discount, " +
                "((OrderDetails.Quantity * OrderDetails.UnitPrice) - OrderDetails.Discount) TotalAmount " +
                "From Orders Join [Order Details] OrderDetails On OrderDetails.OrderID = Orders.OrderID " +
                "Join Products ON Products.ProductID = OrderDetails.ProductID " +
                "JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID " +
                "JOIN Categories ON Products.CategoryID = Categories.CategoryID ", connection);

        connection.Open();
        SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);

        IList<Order> orderList = new List<Order>();
        while (dr.Read())
        {
            Order order = new Order();
            order.OrderID = dr["OrderID"].ToString();
            order.CustomerID = dr["CustomerID"].ToString();
            order.CustomerName = dr["CompanyName"].ToString();
            order.OrderDate = Convert.ToDateTime(dr["OrderDate"]);
            order.UnitPrice = Convert.ToDouble(dr["UnitPrice"]);
            order.Quantity = Convert.ToInt32(dr["Quantity"]);
            order.Discount = Convert.ToDouble(dr["Discount"]);
            order.TotalAmount = Convert.ToDouble(dr["TotalAmount"]);

            orderList.Add(order);
        }
        return orderList;
    }
}

In the code, I use an entity class for holding order details. The Order class defined below.
public class Order
{
    public string OrderID { get; set; }
    public string CustomerID { get; set; }
    public string CustomerName { get; set; }
    public DateTime OrderDate { get; set; }
    public double UnitPrice { get; set; }
    public int Quantity { get; set; }
    public double Discount { get; set; }
    public double TotalAmount { get; set; }
}

By running the code, I got the following screen.

The output of the Excel Sheet will be

download the working example of the source code in C# here and in VB here.

Sunday, 23 October 2011

Exporting the data to Excel sheet with image embedded using Excel Application object



I had blogged some post previously for exporting the data to Excel sheet from ASP.NET page. Even though the basic concept is same, each example can be useful for specific situation.

This post concentrates on another aspect of exporting the data to excel with image where the image stored in database or path of the file. In this example, the image will be loaded and exported to the excel file. So, the excel file can be transferred to any place and not required web server (internet) connection to show the image - the image will show without any issue.

Note :
  1. I am using Northwind database in this example for implementation. So please make sure to have Northwind database and update the configuration in the Web.Config.
  2. This code uses ApplicationClass class in Microsoft.Office.Interop.Excel namespace. So to implement this code, we must add reference to Microsoft.Office.Interop.Excel assembly in the project.
  3. As all the image files are exported to excel, the file size of the excel file will be big.

Preparing the Database:
The Products table in Northwind database has list of Product details. I am planning to use the same table to show list of products with the image in the grid and to export to excel. But the Products table does not contain column for storing the Image. So, I am altering the table to add a new column ProductImage for storing image.

Below is the script for adding ProductImage column in Products table.
alter table Products add ProductImage Image
To update the Image in the table, I added a separate page (UpdateImage.aspx in the source code) in the project which will accept an image and update in the table.The page contains a textbox (ID : txtProductID) and a FileUpload (ID : fuImage) control for getting the ProductID and the image of the product. There is a button control on the page (ID : btnSave) for saving the selected image for the product id entered in the text box.
protected void btnSave_Click(object sender, EventArgs e)
{
    if ((fuImage.PostedFile.FileName.Trim().Length > 0) &&
        (fuImage.PostedFile != null))
    {
        byte[] image = new byte[fuImage.PostedFile.ContentLength];

        fuImage.PostedFile.InputStream.Read(image, 0, (int)fuImage.PostedFile.ContentLength);


        SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString);

        SqlCommand command = new SqlCommand();
        command.CommandText = "Update Products set ProductImage = @Image where ProductID = '" + txtProductID.Text + "'";

        command.CommandType = CommandType.Text;
        command.Connection = connection;

        PrepareSQLParameter(command, "@Image", SqlDbType.Image, image.Length, image);

        connection.Open();

        int result = command.ExecuteNonQuery();
        connection.Close();

        txtProductID.Text = "";
    }
}
private SqlParameter PrepareSQLParameter(SqlCommand command, string parameterName, SqlDbType parameterType, int parameterLength, object parameterValue)
{
    SqlParameter parameter = new SqlParameter(parameterName, parameterType, parameterLength);
    parameter.Value = parameterValue;

    command.Parameters.Add(parameter);
    return parameter;
}
By running this code, I have updated some image for each product. So the table contains image for some products. Below is the query output of the Products table -

Preparing the ASP.NET page:
Now we are ready to show the image in the GridView on the page. Below script and code used for displaying list of products with image in the ASP.NET page. (The code present in Default.aspx in source code)
<asp:Button ID="btnExport" runat="server" Text="Export" onclick="btnExport_Click" />

<asp:GridView ID="grdViewProducts" runat="server" 
    AutoGenerateColumns="False" GridLines="None"
    AllowPaging="True" PageSize="8"
    DataKeyNames="ProductID" Width="100%" CellPadding="4" ForeColor="#333333">
    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
    
    <Columns>
        <asp:BoundField DataField="ProductID" HeaderText="Product ID" />
        <asp:BoundField DataField="ProductName" HeaderText="Product" />
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity Per Unit"/>
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" />
        <asp:TemplateField HeaderText="Photo">
            <ItemStyle Width="10%" HorizontalAlign="Center" />
            <ItemTemplate>
                <img id="imgPhoto" src='<%# "GetImageHandler.ashx?ProductID=" + Eval("ProductID") %>'
                    alt="<%# Eval("ProductName") %>" width="125px" height="125px" 
                    title="<%# Eval("ProductName") %>"/>
            </ItemTemplate>
        </asp:TemplateField>
    </Columns>
    
    <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <EditRowStyle BackColor="#999999" />
    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
The Code behind
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
        BindGrid();
}
private void BindGrid()
{
    grdViewProducts.DataSource = ProductList();
    grdViewProducts.DataBind();
}
public IList<ProductView> ProductList()
{
    using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString))
    {
        SqlCommand command = new SqlCommand(
           "SELECT Top 20 ProductID, ProductName, CompanyName, CategoryName, ProductImage, " +
           //"QuantityPerUnit, UnitPrice, 'GetImageHandler.ashx?ProductID=' + CAST(ProductID AS VARCHAR) ProductImagePath " +
           "QuantityPerUnit, UnitPrice, ProductImagePath " +
           "FROM Products JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID " +
           "JOIN Categories ON Products.CategoryID = Categories.CategoryID " +
           "Order by ProductID", connection);

        connection.Open();
        SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);

        IList<ProductView> productViewList = new List<ProductView>();
        while (dr.Read())
        {
            ProductView productView = new ProductView();
            productView.ProductID = dr["ProductID"].ToString();
            productView.ProductName = dr["ProductName"].ToString();
            productView.CompanyName = dr["CompanyName"].ToString();
            productView.CategoryName = dr["CategoryName"].ToString();
            productView.QuantityPerUnit = dr["QuantityPerUnit"].ToString();
            productView.UnitPrice = Convert.ToDouble(dr["UnitPrice"].ToString());
            productView.ProductImagePath = dr["ProductImagePath"].ToString();
            //productView.ProductImagePath = HttpContext.Current.Request.Url.AbsoluteUri.Substring(0, HttpContext.Current.Request.Url.AbsoluteUri.LastIndexOf("/")) + "/" + dr["ProductImagePath"].ToString();
            productView.IsImageAvailable = ((dr["ProductImage"].ToString().Length > 0) ? true : false);
            productViewList.Add(productView);
        }
        return productViewList;
    }
}
I am using a GenericHandler (GetImageHandler.ashx) for getting the image content to assign in the image control.
public class GetImageHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        using (SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["SQLConnection"].ConnectionString))
        {
            SqlCommand command = new SqlCommand("Select ProductImage from Products where ProductID = '" + context.Request.QueryString["ProductID"].ToString() + "'", connection);

            connection.Open();
            SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);
            while (dr.Read())
            {
                if (dr["ProductImage"].ToString().Length > 0)
                {
                    context.Response.BinaryWrite((byte[])dr["ProductImage"]);
                }
            }
        }
    }
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}
As shown in the code, I used an Entity class ProductView for storing Product Information. The code for ProductView would be:
public class ProductView
{
    public string ProductID { get; set; }
    public string ProductName { get; set; }
    public string CompanyName { get; set; }
    public string CategoryName { get; set; }
    public string QuantityPerUnit { get; set; }
    public double UnitPrice { get; set; }
    public string ProductImagePath { get; set; }
    public bool IsImageAvailable { get; set; }
}
Below figure shows the output of the code execution -

Exporting to Excel sheet with image:
I am using a reusable class which can be used for exporting the data with image to an excel sheet. But only requirement is to provide the input as IList collection for defining the data with image. The c# code for the class will be:
/// <summary>
/// Class to Export the Data to Excel sheet. Input must be a IList colection
/// </summary>
/// <typeparam name="T">Entity</typeparam>
public class ExcelAppExportor<T>
{
    /// <summary>
    /// Constructor
    /// </summary>
    public ExcelAppExportor()
    {
        TemplateFileName = string.Empty;
        IsExportIncludesHeader = true;
        ExportSheetName = "Export";
    }

    /// <summary>
    /// Holds the Data
    /// </summary>
    private IList<T> ListData;

    /// <summary>
    /// Holds the Export Columns
    /// </summary>
    private IList<ExportColumn> ExportColumns;

    /// <summary>
    /// Add the export column
    /// </summary>
    /// <param name="ExportColumn">ExportColumn</param>
    public void AddExportColumn(ExportColumn ExportColumn)
    {
        if (ExportColumns == null) ExportColumns = new List<ExportColumn>();
        ExportColumns.Add(ExportColumn);
    }

    /// <summary>
    /// List contains the list of entity object which are export to excel
    /// </summary>
    public IList<T> BindDataList
    {
        get { return ListData; }
        set { ListData = value; }
    }

    /// <summary>
    /// File Name of the Export output file
    /// </summary>
    public string ExportFileName { get; set; }

    /// <summary>
    /// Template File Name - Using Template file to Export
    /// </summary>
    public string TemplateFileName { get; set; }

    /// <summary>
    /// Sheet name to Export the data
    /// </summary>
    public string ExportSheetName { get; set; }

    /// <summary>
    /// Is the header data needs to be exported
    /// </summary>
    public bool IsExportIncludesHeader { get; set; }

    public void Export()
    {
        try
        {
            #region Filling Export Columns
            // Check the columns to export is mentioned, if not
            if (ExportColumns == null)
            {
                // Create an entity object. If the list count == 0 ??? - needs to be handled from client
                T tEntity = ListData[0];

                // Export the columns to export from the property name
                ExportColumns = new List<ExportColumn>();
                foreach (System.Reflection.PropertyInfo propertyInfo in tEntity.GetType().GetProperties())
                    ExportColumns.Add(new ExportColumn(propertyInfo.Name, propertyInfo.Name));
            }
            #endregion

            // Create excel application
            Application ExcelApp = new ApplicationClass();
            Workbook workbook;
            Sheets sheets;
            Worksheet worksheet;

            // Is Export needs to be exported to a Template file
            if (TemplateFileName != string.Empty)
            {
                #region Load the Template file
                // Load the work book
                workbook = ExcelApp.Workbooks.Open(TemplateFileName, 0, false, 5, "", "", false,
                    XlPlatform.xlWindows, "", true, false, 0, true, false, false);

                sheets = workbook.Sheets;
                worksheet = (Worksheet)sheets.get_Item(1); // To avoid unassigned variable error

                bool IsWorkSheetFound = false;

                //Check is there any worksheet with the name provided. If yes, clear all data inside to fill new data
                for (int intSheetIndex = 1; intSheetIndex <= sheets.Count; intSheetIndex++)
                {
                    worksheet = (Worksheet)sheets.get_Item(intSheetIndex);
                    if (worksheet.Name.ToString().Equals(ExportSheetName))
                    {
                        IsWorkSheetFound = true;
                        break;
                    }
                }

                // If No work sheet found, add it at the last
                if (!IsWorkSheetFound)
                {
                    worksheet = (Worksheet)workbook.Sheets.Add(
                        Type.Missing, (Worksheet)sheets.get_Item(sheets.Count),
                        Type.Missing, Type.Missing);
                    worksheet.Name = ExportSheetName;
                }
                #endregion
            }
            else
            {
                #region Crate the Template File
                // Adding new work book
                workbook = ExcelApp.Workbooks.Add(XlWBATemplate.xlWBATWorksheet);

                sheets = workbook.Sheets;

                worksheet = (Worksheet)sheets.get_Item(1);

                worksheet.Name = ExportSheetName;
                #endregion
            }
            int intCol = 0;

            #region Populating the Header
            bool IsImageColumnPresent = false;
            // If the header needs to exported. In templated files, normally we will have the headings
            if (IsExportIncludesHeader == true)
            {
                // Exporting Header
                foreach (ExportColumn exportColumn in ExportColumns)
                {
                    Range range = (Range)worksheet.Cells[exportColumn.StartRowIndex++, ((exportColumn.ExcelColumnIndex == 0) ? ++intCol : exportColumn.ExcelColumnIndex)];
                    range.Select();
                    range.Value2 = exportColumn.HeaderText.ToString();
                    if (exportColumn.ValueFormat == "Image")
                    {
                        IsImageColumnPresent = true;
                        range.ColumnWidth = exportColumn.ImageColumnWidth;
                    }
                    else
                        range.Columns.EntireColumn.AutoFit();

                    range.Font.Bold = true;
                }
            }
            #endregion
            string strTempImagePath = HttpContext.Current.Request.PhysicalApplicationPath + Guid.NewGuid() + @"\";

            if (IsImageColumnPresent == true)
                System.IO.Directory.CreateDirectory(strTempImagePath);

            // Exporting Data
            foreach (T tEntity in BindDataList)
            {
                intCol = 0;
                foreach (ExportColumn exportColumn in ExportColumns)
                {
                    Range range = (Range)worksheet.Cells[exportColumn.StartRowIndex++, ((exportColumn.ExcelColumnIndex == 0) ? ++intCol : exportColumn.ExcelColumnIndex)];
                    range.Select();
                    if (exportColumn.ValueFormat == "Image")
                    {
                        string strImagePath = tEntity.GetType().GetProperty(exportColumn.ColumnName.ToString()).GetValue(tEntity, null).ToString();

                        Image image = null;
                        if (strImagePath.StartsWith("http"))
                            image = DownloadImage(strImagePath);
                        else
                            image = (strImagePath.Trim().Length > 0) ? (Image)Image.FromFile(strImagePath, true) : null;
                        if (image != null)
                        {
                            strImagePath = strTempImagePath + Guid.NewGuid() + @".jpg";

                            if (image != null)
                                image.Save(strImagePath);
                            range.RowHeight = exportColumn.ImageHeight;
                            range.ColumnWidth = exportColumn.ImageColumnWidth;
                            worksheet.Shapes.AddPicture(strImagePath, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoCTrue, ((float)Convert.ToDecimal(range.Left)) + 1, ((float)Convert.ToDecimal(range.Top)) + 1, exportColumn.ImageWidth - 2, exportColumn.ImageHeight - 2);
                        }
                    }
                    else
                    {
                        range.Value2 = tEntity.GetType().GetProperty(exportColumn.ColumnName.ToString()).GetValue(tEntity, null).ToString();
                        range.Columns.EntireColumn.AutoFit();
                    }
                }
            }
            intCol = 0;
            foreach (ExportColumn exportColumn in ExportColumns)
            {
                Range range = (Range)worksheet.Cells[exportColumn.StartRowIndex++, ((exportColumn.ExcelColumnIndex == 0) ? ++intCol : exportColumn.ExcelColumnIndex)];
                if (exportColumn.ValueFormat != string.Empty)
                    range.Columns.EntireColumn.NumberFormat = exportColumn.ValueFormat;
            }
            try
            {
                Range range = (Range)worksheet.Cells[1, 1];
                range.Select();
                
                // Save the workbook with saving option
                workbook.Close(true, ExportFileName, Type.Missing);
                ExcelApp.UserControl = false;
                ExcelApp.Quit();
                ExcelApp = null;

                System.IO.Directory.Delete(strTempImagePath, true);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            Process[] ps = Process.GetProcesses();
            foreach (Process p in ps)
            {
                if (p.ProcessName.ToLower().Equals("excel"))
                    p.Kill();
            }
        }
    }
    /// <summary>
    /// Function to download Image from website
    /// Method from http://www.digitalcoding.com/Code-Snippets/C-Sharp/C-Code-Snippet-Download-Image-from-URL.html
    /// Thanks to digitialcoding.com
    /// </summary>
    /// <param name="_URL">URL address to download image</param>
    /// <returns>Image</returns>
    public Image DownloadImage(string _URL)
    {
        Image _tmpImage = null;

        try
        {
            // Open a connection
            System.Net.HttpWebRequest _HttpWebRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(_URL);

            _HttpWebRequest.AllowWriteStreamBuffering = true;

            // You can also specify additional header values like the user agent or the referer: (Optional)
            _HttpWebRequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)";
            _HttpWebRequest.Referer = "http://www.google.com/";

            // set timeout for 20 seconds (Optional)
            _HttpWebRequest.Timeout = 20000;

            // Request response:
            System.Net.WebResponse _WebResponse = _HttpWebRequest.GetResponse();

            // Open data stream:
            System.IO.Stream _WebStream = _WebResponse.GetResponseStream();

            // convert webstream to image
            _tmpImage = Image.FromStream(_WebStream);

            // Cleanup
            _WebResponse.Close();
            _WebResponse.Close();
        }
        catch (Exception _Exception)
        {
            // Error
            Console.WriteLine("Exception caught in process: {0}", _Exception.ToString());
            return null;
        }

        return _tmpImage;
    }
}
I have a button in the page which will trigger the exporting functionality. In this event, I have code for getting the data as IList and calling the Export class object by passing the IList collection as input.
protected void btnExport_Click(object sender, EventArgs e)
{
    try
    {
        ExcelAppExportor<ProductView> exportor = new ExcelAppExportor<ProductView>();
        exportor.BindDataList = ProductList();
        exportor.AddExportColumn(new ExportColumn("ProductID", "Product ID", 1, 1));
        exportor.AddExportColumn(new ExportColumn("ProductName", "Product Name", 1, 2));
        exportor.AddExportColumn(new ExportColumn("CompanyName", "Company Name", 1, 3));
        exportor.AddExportColumn(new ExportColumn("CategoryName", "Category Name", 1, 4));
        exportor.AddExportColumn(new ExportColumn("QuantityPerUnit", "Quantity Per Unit", 1, 5));
        exportor.AddExportColumn(new ExportColumn("UnitPrice", "Unit Price", 1, 6));
        exportor.AddExportColumn(new ExportColumn("IsImageAvailable", "Is Image Available", 1, 7));
        exportor.AddExportColumn(new ExportColumn("ProductImagePath", "Image", "Image", 1, 8, 100, 80, 20));

        string strExportFileName = Path.Combine(@"C:\Temp", Guid.NewGuid().ToString("N")) + ".xls";

        exportor.ExportFileName = strExportFileName;
        exportor.IsExportIncludesHeader = true;
        exportor.ExportSheetName = "Orders";
        exportor.Export();

        byte[] ExcelStream = File.ReadAllBytes(strExportFileName);

        Context.Response.ClearContent();
        Context.Response.ContentType = "application/ms-excel";
        Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "Product List"));
        Context.Response.Charset = "";
        Context.Response.BinaryWrite(ExcelStream);
        Context.Response.End();
    }
    catch (Exception ex)
    {
    }
}
The code for adding the export column -
/// <summary>
/// Holds the details of te columns to be exported to Excel from IList
/// </summary>
public class ExportColumn
{
    /// <summary>
    /// Entity object(class) property name, mandatory
    /// </summary>
    public string ColumnName { get; set; }

    /// <summary>
    /// What needs to be shows in the Excel sheet for that col, mandatory
    /// </summary>
    public string HeaderText { get; set; }

    /// <summary>
    /// The format of the value. To get the format string, from the excel sheet -> Column -> Format Cells -> Number -> Custom -> Take the Type
    /// </summary>
    public string ValueFormat { get; set; }

    /// <summary>
    /// From which row the data needs to be exported, for template file it will be useful
    /// </summary>
    public int StartRowIndex { get; set; }

    /// <summary>
    /// On which column the data needs to be exports, So it can be any column. Useful for Templated file
    /// Needs to assign the Index of the column starting by 1
    /// A - 1, B - 2, C - 3 etc., Counting will include the hidden column
    /// </summary>
    public int ExcelColumnIndex { get; set; }

    /// <summary>
    /// Height of the Image
    /// </summary>
    public int ImageHeight { get; set; }

    /// <summary>
    /// Width of the Image
    /// </summary>
    public int ImageWidth { get; set; }

    /// <summary>
    /// Width of the image column
    /// </summary>
    public int ImageColumnWidth { get; set; }

    public ExportColumn()
    {
        StartRowIndex = 1;
        this.ValueFormat = string.Empty;
        this.StartRowIndex = 1;
        this.ExcelColumnIndex = 0;
    }
    public ExportColumn(string ColumnName, string HeaderText)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = string.Empty;
        this.StartRowIndex = 1;
        this.ExcelColumnIndex = 0;
    }
    public ExportColumn(string ColumnName, string HeaderText, int StartRowIndex, int ExcelColumnIndex)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = string.Empty;
        this.StartRowIndex = StartRowIndex;
        this.ExcelColumnIndex = ExcelColumnIndex;
    }
    public ExportColumn(string ColumnName, string HeaderText, string ValueFormat)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = ValueFormat;
        this.StartRowIndex = 1;
        this.ExcelColumnIndex = 0;
    }
    public ExportColumn(string ColumnName, string HeaderText, string ValueFormat, int StartRowIndex, int ExcelColumnIndex)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = ValueFormat;
        this.StartRowIndex = StartRowIndex;
        this.ExcelColumnIndex = ExcelColumnIndex;
    }
    public ExportColumn(string ColumnName, string HeaderText, string ValueFormat, int StartRowIndex, int ExcelColumnIndex, int ImageWidth, int ImageHeight, int ImageColumnWidth)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
        this.ValueFormat = ValueFormat;
        this.StartRowIndex = StartRowIndex;
        this.ExcelColumnIndex = ExcelColumnIndex;
        this.ImageHeight = ImageHeight;
        this.ImageWidth = ImageWidth;
        this.ImageColumnWidth = ImageColumnWidth;
    }
}
Adding the ExportColumn to ExcelAppExportor object
The ExportColumn class is used for adding the exporting column name, header text and format of the data. The format of the data should as per the format defined in excel sheet (Select the column in Excel Sheet -> right click the column -> select Format Cells... -> Define any format -> Select the Custom -> tab -> Copy the Type value).

  1. ExportColumn(string ColumnName, string HeaderText) - Defining column name of the database table/view/query and the column header text on the Excel Sheet.
  2. ExportColumn(string ColumnName, string HeaderText, int StartRowIndex, int ExcelColumnIndex) - Defining column name, column header text, row index where the data should start to export, column index where the data should start to export.
  3. ExportColumn(string ColumnName, string HeaderText, string ValueFormat) - Defining column name, column header text, value format of the data.
  4. ExportColumn(string ColumnName, string HeaderText, string ValueFormat, int StartRowIndex, int ExcelColumnIndex) - Defining column name, column header text, value format of the data, row index where the data should start to export, column index where the data should start to export.
  5. ExportColumn(string ColumnName, string HeaderText, string ValueFormat, int StartRowIndex, int ExcelColumnIndex, int ImageWidth, int ImageHeight, int ImageColumnWidth) - Defining column name, column header text, value format of the data, row index where the data should start to export, column index where the data should start to export, image width, image height, image column width.

Here, the last ExportColumn constructor is used for adding the information about the image column.The image url path can be defined in two ways:
  1. The physical path of the file. - For Ex: D:\ImageExportTest\WebApp\Images\Image01.jpg. So the code assign the physical path as defined below
    productView.ProductImagePath = @"D:\ImageExportTest\WebApp\Images\Image01.jpg"
    Note: The image files must be readable from the application.
  2. The http/https url of the path of the file - For Ex: http://localhost/images/world.jpg. So the code assign the value as defined below
    productView.ProductImagePath = @"http://localhost/images/world.jpg"

Download the working example in C# here and in VB here.

The screen shot of the exported excel sheet for the example defined above:

Wednesday, 28 September 2011

Exporting to Excel from GridView (Adding & Removing columns with all rows)



Previous post of Exporting to Excel from GridView (with visible columns and all rows) shows an example for a normal way of exporting to excel with only visible columns and all rows. But in sometimes, business required showing only some important columns and exporting all possible columns in the Excel.

As I already explained in first post of this series, all those examples are following same way of exporting to Excel. But there will be some tricks to achieve our goals.

Considering our requirement, we can achieve exporting invisible and visible columns in many ways. One of an easy way is to have two GridView in a same page, one is to show to the user with required columns on the screen and another is to export with all possible columns to the excel (this Grid will always be invisible and will not seen by the user). So when an Export button clicked, the code will bind the records to the invisible GridView (which has all possible columns) and export the records to the Excel.

Another way to do the same is to have a class which creates GridView (or a table) on the fly using code behind and populate the data to export it. This example will be explained in the following post of this series.

Note: I am using Northwind database in this example for binding records. So make sure you setup the same database and change the connection string in the Web.Config before testing the working example.

Let us implement the first way “place two Gridview in same page; one is to show in the screen and another one is to export”.

The GridView used to show some particular columns in the screen.
<asp:GridView ID="grdViewProducts" runat="server"
    AllowPaging="true" AutoGenerateColumns="False" TabIndex="1"
    DataKeyNames="ProductID" Width="100%" ShowFooter="False"
    CellPadding="4" ForeColor="#333333" GridLines="Both" >
    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name" />
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity Per Unit"/>
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" />
    </Columns>
    <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <EditRowStyle BackColor="#999999" />
    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
The GridView used to export all possible columns to the Excel sheet.
<asp:GridView ID="grdViewExport" runat="server"
    AllowPaging="false" AutoGenerateColumns="False" TabIndex="1"
    DataKeyNames="ProductID" Width="100%" ShowFooter="False"
    CellPadding="4" ForeColor="#333333" GridLines="Both" >
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name" />
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity Per Unit"/>
        <asp:BoundField DataField="UnitPrice" HeaderText="Unit Price" />
        <asp:BoundField DataField="UnitsInStock" HeaderText="Units In Stock" />
        <asp:BoundField DataField="UnitsOnOrder" HeaderText="Units On Order" />
        <asp:BoundField DataField="ReorderLevel" HeaderText="Reorder Level" />
        <asp:BoundField DataField="Discontinued" HeaderText="Discontinued" />
    </Columns>
    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
<asp:Button ID="btnExport" runat="server" Text="Export" onclick="btnExport_Click" />

Note: The second (used for exporting) GridView AllowPaging property set to False. So it will export all the records and by default the export GridView will not have any records. The data for the export GridView will be bound on demand and export it.

The code behind for binding the records to the GridView.
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        BindGrid(grdViewProducts);
    }
}
/// <summary>
/// Method which binds the data to the Grid
/// </summary>
private void BindGrid(GridView gridControl)
{
    using (SqlConnection connection =
        new SqlConnection(ConfigurationManager.ConnectionStrings
                            ["SQLConnection"].ConnectionString))
    {

        SqlDataAdapter adaptor = new SqlDataAdapter(
               "SELECT ProductID, ProductName, CompanyName, CategoryName, " +
               "QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued " +
               "FROM Products JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID " +
               "JOIN Categories ON Products.CategoryID = Categories.CategoryID ", connection);

        DataSet ds = new DataSet();

        adaptor.Fill(ds);
        gridControl.DataSource = ds;
        gridControl.DataBind();
    }
}
/// <summary>
/// Event for exporting to Excel
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnExport_Click(object sender, EventArgs e)
{
    grdViewProducts.AllowPaging = false;
    BindGrid(grdViewExport);

    Context.Response.ClearContent();
    Context.Response.ContentType = "application/ms-excel";
    Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "ExcelFileName"));
    Context.Response.Charset = "";
    System.IO.StringWriter stringwriter = new System.IO.StringWriter();
    HtmlTextWriter htmlwriter = new HtmlTextWriter(stringwriter);
    grdViewExport.RenderControl(htmlwriter);
    Context.Response.Write(stringwriter.ToString());
    Context.Response.End();
}
/// <summary>
/// This event is used to remove the error occuring while exporting to export
/// The Error is : Control 'ControlID' of type 'GridView' must be placed inside a form tag with runat=server.
/// </summary>
/// <param name="control"></param>
public override void VerifyRenderingInServerForm(Control control)
{
    return;
}

Second Implementation for the same requirement

Let us take an example of another scenario; the GridView contains some columns with Edit, Delete columns. The Edit, Delete column is used for doing some operation on the particular record but those columns are not required when exporting to the Excel.

The implementation follows.

The GridView scripts.
<asp:GridView ID="grdViewProducts" runat="server"
    AllowPaging="True" AutoGenerateColumns="False" TabIndex="1"
    DataKeyNames="ProductID" Width="100%"
    CellPadding="4" ForeColor="#333333" >
    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name" />
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity Per Unit"/>
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" />
        <asp:CommandField HeaderText="Select" ShowSelectButton="True">
            <ItemStyle HorizontalAlign="Center" />
        </asp:CommandField>
        <asp:CommandField HeaderText="Delete" ShowDeleteButton="True">
            <ItemStyle HorizontalAlign="Center" />
        </asp:CommandField>
    </Columns>
    <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <EditRowStyle BackColor="#999999" />
    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
 
<asp:GridView ID="grdViewExport" runat="server"
    AllowPaging="false" AutoGenerateColumns="False" TabIndex="1"
    DataKeyNames="ProductID" Width="100%" ShowFooter="False"
    CellPadding="4" ForeColor="#333333" GridLines="Both" >
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name" />
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity Per Unit"/>
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" />
    </Columns>
    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
There are two GridView defined here, one is to show on the screen with Edit and Delete column and another one is to export to Excel.

The code behind will be almost same as above implementation.

Below is the screenshot of the output of this example.

GridView shows only five columns

Exported sheet from GridView has nine columns (ref - Previous image)

GridView with Select and Delete columns

Exported sheet by removing Select and Delete columns

Download the working code of this example
First example (ExportGridViewAdditionalCol) - C# here and VB here.
Second example (ExportExcelRemoveCols) - C# here and VB here.

Exporting to Excel from GridView (All columns and rows - Normal Method)



In this post, I am giving an example of exporting all the rows from GridView with only the columns what it shown on the page. So, the exported Excel sheet will be as like GridView with all the rows. This is a normal exporting functionality all of us do mostly in ASP.NET page.

This example works with Northwind database. So make sure you setup Northwind database in your SQL Server and change the connection string in Web.Config. The GridView shows list of products with multiple pages and an Export button in the page trigger an event to export the GridView data with the same format to Excel.

The ASPX script:
<asp:GridView ID="grdViewProducts" runat="server"
    AllowPaging="true" AutoGenerateColumns="False" TabIndex="1"
    DataKeyNames="ProductID" Width="100%" ShowFooter="False" 
    CellPadding="4" ForeColor="#333333" GridLines="Both" >
    <RowStyle BackColor="#F7F6F3" ForeColor="#333333" />
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product Name" />
        <asp:BoundField DataField="CompanyName" HeaderText="Supplier" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Quantity Per Unit"/>
        <asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" DataFormatString="{0:#0.00}" />
    </Columns>
    <FooterStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <PagerStyle BackColor="#284775" ForeColor="White" HorizontalAlign="Right" />
    <SelectedRowStyle BackColor="#E2DED6" Font-Bold="True" ForeColor="#333333" />
    <HeaderStyle BackColor="#5D7B9D" Font-Bold="True" ForeColor="White" />
    <EditRowStyle BackColor="#999999" />
    <AlternatingRowStyle BackColor="White" ForeColor="#284775" />
</asp:GridView>
<asp:Button ID="btnExport" runat="server" Text="Export" onclick="btnExport_Click" />

Code behind
I am binding the data to the GridView on first Page load.
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        BindGrid();
    }
}

/// <summary>
/// Method which binds the data to the Grid
/// </summary>
private void BindGrid()
{
    using (SqlConnection connection =
        new SqlConnection(ConfigurationManager.ConnectionStrings
                            ["SQLConnection"].ConnectionString))
    {

        SqlCommand command = new SqlCommand(
               "SELECT ProductID, ProductName, CompanyName, CategoryName, " +
               "QuantityPerUnit, UnitPrice FROM Products " +
               "JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID " +
               "JOIN Categories ON Products.CategoryID = Categories.CategoryID ", connection);

        connection.Open();
        SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);

        IList<ProductView> productViewList = new List<ProductView>();
        while (dr.Read())
        {
            ProductView productView = new ProductView();
            productView.ProductID = dr["ProductID"].ToString();
            productView.ProductName = dr["ProductName"].ToString();
            productView.CompanyName = dr["CompanyName"].ToString();
            productView.CategoryName = dr["CategoryName"].ToString();
            productView.QuantityPerUnit = dr["QuantityPerUnit"].ToString();
            productView.UnitPrice = Convert.ToDouble(dr["UnitPrice"].ToString());
            productViewList.Add(productView);
        }
        grdViewProducts.DataSource = productViewList;
        grdViewProducts.DataBind();
    }
}
Code for exporting to Excel. The comments of each method explains what it does.
/// <summary>
/// Event for exporting to Excel
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnExport_Click(object sender, EventArgs e)
{
    grdViewProducts.AllowPaging = false;
    BindGrid();

    PrepareGridViewForExport(grdViewProducts);

    Context.Response.ClearContent();
    Context.Response.ContentType = "application/ms-excel";
    Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "ExcelFileName"));
    Context.Response.Charset = "";
    System.IO.StringWriter stringwriter = new System.IO.StringWriter();
    HtmlTextWriter htmlwriter = new HtmlTextWriter(stringwriter);
    grdViewProducts.RenderControl(htmlwriter);
    Context.Response.Write(stringwriter.ToString());
    Context.Response.End();
}

/// <summary>
/// This event is used to verify the form control is rendered 
/// It is used to remove the error occuring while exporting to export
/// The Error is : Control 'XXX' of type 'GridView' must be placed inside a form tag with runat=server.
/// </summary>
/// <param name="control"></param>
public override void VerifyRenderingInServerForm(Control control)
{
    return;
}

/// <summary>
/// Replace any container controls with literals
/// like Hyperlink, ImageButton, LinkButton, DropDown, ListBox to literals
/// </summary>
/// <param name="gridView">GridView</param>
private void PrepareGridViewForExport(Control gridView)
{
    for (int i = 0; i < gridView.Controls.Count; i++)
    {
        //Get the control
        Control currentControl = gridView.Controls[i];
        if (currentControl is LinkButton)
        {
            gridView.Controls.Remove(currentControl);
            gridView.Controls.AddAt(i, new LiteralControl((currentControl as LinkButton).Text));
        }
        else if (currentControl is ImageButton)
        {
            gridView.Controls.Remove(currentControl);
            gridView.Controls.AddAt(i, new LiteralControl((currentControl as ImageButton).AlternateText));
        }
        else if (currentControl is HyperLink)
        {
            gridView.Controls.Remove(currentControl);
            gridView.Controls.AddAt(i, new LiteralControl((currentControl as HyperLink).Text));
        }
        else if (currentControl is DropDownList)
        {
            gridView.Controls.Remove(currentControl);
            gridView.Controls.AddAt(i, new LiteralControl((currentControl as DropDownList).SelectedItem.Text));
        }
        else if (currentControl is CheckBox)
        {
            gridView.Controls.Remove(currentControl);
            gridView.Controls.AddAt(i, new LiteralControl((currentControl as CheckBox).Checked ? "True" : "False"));
        }
        if (currentControl.HasControls())
        {
            // if there is any child controls, call this method to prepare for export
            PrepareGridViewForExport(currentControl);
        }
    }
}
The ProductView entity class for holding the data of each row while binding.
public class ProductView
{
    public string ProductID { get; set; }
    public string ProductName { get; set; }
    public string CompanyName { get; set; }
    public string CategoryName { get; set; }
    public string QuantityPerUnit { get; set; }
    public double UnitPrice { get; set; }
}
In this example, once the Export Button clicked, we are changing the AllowPaging property to false as we required to export all the data in a single page to Excel sheet. So it exports all the rows which bind to the GridView with all the visible columns.

Below image shows the GridView and exported Excel sheet
GridView on the page

Exported data in Excel sheet

download the working example of source code in C# here and in VB here.

Exporting to Excel from GridView (Using a custom class without GridView)



In previous post of this export to Excel from GridView series we had discussed about how to export data which is not present in the Gridview or to remove some of the columns.

But the examples contain two GridView, one to show on the screen and another one is to export to excel. But instead of having one separate GridView for export, better to consider some other way to avoid it.

In this post, I am planning to provide an example which has a custom class that gets the data set as input and export to Excel without having any separate GridView for exporting functionality. The custom class will create a GridView dynamically on the code and export it.

This example also provides a way to export to Excel in ASP.NET applications without having any GridView or any other data controls present on the screen.

Below is the custom class (Exporter.cs) used for exporting the data to excel.
public class Exporter<T> : System.Web.UI.Page
{
    IList<T> ListData;

    IList<ExportColumn> ExportColumns;

    public void AddExportColumn(string ColumnName, string HeaderText)
    {
        if (ExportColumns == null) ExportColumns = new List<ExportColumn>();
        ExportColumns.Add(new ExportColumn(ColumnName, HeaderText));
    }

    public IList<T> BindDataList
    {
        get { return ListData; }
        set { ListData = value; }
    }
    public void Export()
    {
        try
        {
            GridView gvExportExcel = new GridView();
            gvExportExcel.ID = "ExportExcel";

            if (ExportColumns.Count > 0)
            {
                foreach (ExportColumn exportColumn in ExportColumns)
                {
                    BoundField field = new BoundField();
                    if (exportColumn.ColumnName != string.Empty) field.DataField = exportColumn.ColumnName;
                    if (exportColumn.HeaderText != string.Empty) field.HeaderText = exportColumn.HeaderText;
                    gvExportExcel.Columns.Add(field);
                }
                gvExportExcel.AutoGenerateColumns = false;
            }
            else
                gvExportExcel.AutoGenerateColumns = true;

            gvExportExcel.DataSource = ListData;
            
            gvExportExcel.DataBind();
            
            PrepareGridViewForExport(gvExportExcel);

            Context.Response.ClearContent();
            Context.Response.ContentType = "application/ms-excel";
            Context.Response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", "Sheet1"));
            Context.Response.Charset = "";

            System.IO.StringWriter stringwriter = new System.IO.StringWriter();
            System.Web.UI.HtmlTextWriter htmlwriter = new System.Web.UI.HtmlTextWriter(stringwriter);
            gvExportExcel.RenderControl(htmlwriter);
            Context.Response.Write(stringwriter.ToString());
            Context.Response.End();
        }
        catch (Exception ex)
        {
        }
    }

    /// <summary>
    /// Replace any container controls with literals
    /// like Hyperlink, ImageButton, LinkButton, DropDown, ListBox to literals
    /// </summary>
    /// <param name="gridView">GridView</param>
    private void PrepareGridViewForExport(Control gridView)
    {
        for (int i = 0; i < gridView.Controls.Count; i++)
        {
            Control currentControl = gridView.Controls[i];
            if (currentControl is CheckBox)
            {
                gridView.Controls.Remove(currentControl);
                gridView.Controls.AddAt(i, new LiteralControl((currentControl as CheckBox).Checked ? "True" : "False"));
            }
            if (currentControl.HasControls())
            {
                PrepareGridViewForExport(currentControl);
            }
        }
    }
}
public class ExportColumn
{
    public string ColumnName { get; set; }
    public string HeaderText { get; set; }

    public ExportColumn(string ColumnName, string HeaderText)
    {
        this.ColumnName = ColumnName;
        this.HeaderText = HeaderText;
    }
}

This class inherited from System.Web.UI.Page, so all the functionality we do with aspx page can be done here. This class accepts a List object as input which contains list of entity objects to represent the data on the excel sheet. I also have AddExportColumn for adding the columns to be exported in to the excel.

I have a button used for triggering the export.
<asp:Button ID="btnExport" runat="server" Text="Export Product Data" 
            onclick="btnExport_Click" />
The code behind
protected void btnExport_Click(object sender, EventArgs e)
{
    Exporter<Product> exportor = new Exporter<Product>();
    exportor.BindDataList = ProductList();
    exportor.AddExportColumn("ProductName", "Product Name");
    exportor.AddExportColumn("SupplierName", "Supplier Name");
    exportor.AddExportColumn("UnitPrice", "Unit Price");
    
    exportor.Export();
}

/// <summary>
/// Method which binds the data to the Grid
/// </summary>
private IList<Product> ProductList()
{
    using (SqlConnection connection =
        new SqlConnection(ConfigurationManager.ConnectionStrings
                            ["SQLConnection"].ConnectionString))
    {

        SqlCommand command = new SqlCommand(
               "SELECT ProductID, ProductName, CompanyName, CategoryName, " +
               "QuantityPerUnit, UnitPrice FROM Products " +
               "JOIN Suppliers ON Products.SupplierID = Suppliers.SupplierID " +
               "JOIN Categories ON Products.CategoryID = Categories.CategoryID ", connection);

        connection.Open();
        SqlDataReader dr = command.ExecuteReader(CommandBehavior.CloseConnection);

        IList<Product> productList = new List<Product>();
        while (dr.Read())
        {
            Product product = new Product();
            product.ProductID = dr["ProductID"].ToString();
            product.ProductName = dr["ProductName"].ToString();
            product.SupplierName = dr["CompanyName"].ToString();
            product.CategoryName = dr["CategoryName"].ToString();
            product.QuantityPerUnit = dr["QuantityPerUnit"].ToString();
            product.UnitPrice = Convert.ToDouble(dr["UnitPrice"]);
            product.UnitsInStock = Convert.ToInt32(dr["UnitPrice"]);
            product.UnitsOnOrder = Convert.ToInt32(dr["UnitPrice"]);
            product.ReorderLevel = Convert.ToInt32(dr["UnitPrice"]);
            product.Discontinued = Convert.ToBoolean(dr["UnitPrice"]);

            productList.Add(product);
        }
        return productList;
    }
}

The entity class - Product.cs
public class Product
{
        public string ProductID { get; set; }
        public string ProductName { get; set; }
        public string SupplierName { get; set; }
        public string CategoryName { get; set; }
        public string QuantityPerUnit { get; set; }
        public double UnitPrice { get; set; }
}
As you seen in this example, I have no GridView placed on the page, but I am getting the data exported.

Below is the output of the screen

Page with single button

Exported data in Excel sheet

Download the working example of the source in C# here and in VB here.