13 January, 2009

Exporting Data From AJAX GridView with Image to Excel Sheet



Introduction



Generally, exporting GridView data to Excel is a widely used concept in Asp.Net web application development. So this article glow some light on it by combining various real-time situations listed below.

  • Export complete GridView data to Excel

  • Export GridView data from Selected Page to Excel

  • Export GridView Randomly Selected Rows To Excel

  • Export GridView with Image column to Excel

  • Export GridView with Decimals and DateTime columns with a pre-defined custom format to Excel


For this purpose, we place a GridView control in the Aspx Page and display some Customer information with their favorite images in a column. The GridView control will have columns such as Customer Name [Cus_Name], Customer Age [Cus_Age], Customer Email [Cus_Email], Some Random Decimals numbers [SomeDecimals], Date [Today] and Favorite Image [ImgUrl]. Specify the GridView control’s AllowPaging as true, AutoGenerateColumns as false and the DataKeyNames as “Cus_Code”. Then place three buttons, below the GridView control; change the Text of the first button as “Export All Rows”, second button as “Export Selected Rows” and third button as “Export Current Page”.

Design GridView Control

The GridView’s first column will be TemplateField column, which contains a Checkbox in the ItemTemplate section. Change the Id of the Checkbox as “chkSelect”. This is to select any GridView row and to import to the Excel Sheet. From second to fifth column will be a normal BoundField column to display Customer Name, Age, Email, decimals and date. The last column will be another TemplateField column with an Image control placed in it and the ImageUrl property will be bind to display the Customer’s favorite Image.

Include AJAX

Drag and drop a ScriptManager and an UpdatePanel into the Aspx page and move the GridView and the three buttons inside it. For exporting data into excel sheet, we need an explicit post back. Since we need to achieve this together with AJAX, we have to add PostBackTrigger into the UpdatePanel’s Triggers tag. So add three PostBackTrigger and specify the ControlID to Button1, Button2 and Button3 respectively. The UpdatePanelTrigger Collection Editor is shown below.
The complete Html source of the GridView control inside UpdatePanel will look like below.

<asp:UpdatePanel id="UpdatePanel1" runat="server">

<contenttemplate>

<TABLE width="100%"><TR><TD>

<asp:GridView id="GridView1" runat="server" Width="100%"

OnPageIndexChanging="GridView1_PageIndexChanging"

PageSize="10" AllowPaging="True"
AutoGenerateColumns="False"

OnRowDataBound="GridView1_RowDataBound"

DataKeyNames="Cus_Code">

<Columns>

<asp:TemplateField HeaderText="Select">

<ItemTemplate>

<asp:CheckBox id="chkSelect" runat="server"></asp:CheckBox>

</ItemTemplate>

</asp:TemplateField>

<asp:BoundField DataField="Cus_Name" HeaderText="Name"></asp:BoundField>

<asp:BoundField DataField="Cus_Age" HeaderText="Age"></asp:BoundField>

<asp:BoundField DataField="Cus_Email" HeaderText="Email"></asp:BoundField>

<asp:BoundField DataField="SomeDecimals" HeaderText="Decimals"></asp:BoundField>
<asp:BoundField DataField="Today" HeaderText="Date"></asp:BoundField>

<asp:TemplateField HeaderText="Favorites">

<ItemTemplate>

<asp:Image id="Image1" runat="server" ImageUrl='<%# Bind("ImgUrl")
%>' ></asp:Image>

</ItemTemplate>

</asp:TemplateField>

</Columns>

</asp:GridView>

</TD></TR>

<TR><TD>

<TABLE width="100%"><TR> <TD width="25%">

<asp:Button id="Button1" onclick="Button1_Click" runat="server"
Text="Export All Rows"></asp:Button></TD>

<TD width="25%">

<asp:Button id="Button2" onclick="Button2_Click" runat="server"
Text="Export Selected Rows"></asp:Button>

</TD>

<TD width="25%">

<asp:Button id="Button3" onclick="Button3_Click" runat="server"
Text="Export Current Page"></asp:Button>

</TD></TR>

</TABLE>

</TD></TR>

</TABLE>

</contenttemplate>

<triggers>

<asp:PostBackTrigger ControlID="Button1"></asp:PostBackTrigger>

<asp:PostBackTrigger ControlID="Button2"></asp:PostBackTrigger>

<asp:PostBackTrigger ControlID="Button3"></asp:PostBackTrigger>

</triggers>

</asp:UpdatePanel>

Before making Export to Excel functionality, we have to set up the GridView control,


1. Bind GridView control to Customer Table

2. Allow GridView paging

3. Preserve the selected checkbox values when user navigates to other pages.

1. Bind GridView control to Customer Table
private void BindGrid()

{

  string sql = "Select Cus_Code, Cus_Name, Cus_Age, Cus_Email, SomeDecimals,
ImgUrl, Today 

    from Customer Order By Cus_Name"; 

  SqlDataAdapter da = new SqlDataAdapter(sql, “YourConnectionString”); 

  DataTable dt = new DataTable(); 

  da.Fill(dt); 

  GridView1.DataSource = dt; 

  GridView1.DataBind();

}


Write a private method BindGrid to retrieve data from the Customer table, and bind it with the GridView control. This is a common method that can be used throughout the page to bind the GridView control. Call this method in the Page Load event as follows.

protected void Page_Load(object sender, EventArgs e)

{

  if (!IsPostBack)

  {

     BindGrid(); 

  }

}

2. Allow Paging by adding PageIndexChanging event

For the purpose of this article, we have collaborated both paging and sorting for a single GridView control. So every section of the source code explained below will cover both custom paging and sorting concept. Just concentrate bit more from here. For the purpose of custom paging, we are creating an instance of PagedDataSource class as follows


protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)

{

  GetCheckedList(); 

  GridView1.PageIndex = e.NewPageIndex; 

  BindGrid();

}

We have already set the AllowPaging property of GridView control as “true”. So the PageIndexChanging event will have normal paging code, but it call GetCheckedList method before setting the PageIndex. The purpose GetCheckedList method is to store the “selected” Checkboxes values in a temporary ArrayList variable. So when the user navigates to other pages, we store the values of the “selected” checkbox in the ArrayList and to retain it throughout the page, we store it in a ViewState. If the user again comes to the visited page, we keep the Checkbox as “selected”, which they previously selects. This is explained clearly in the next point.

3. Preserve the “selected” Checkbox value when navigates to other pages

This is achieved by an ArrayList and a ViewState. ArrayList will have the collection of “selected” Checkboxes and it is stored in a ViewState for retrival purpose. So Declare an ArrayList variable in page scope as follows.


using System.IO;

ArrayList arrlist = new ArrayList();


In the GetCheckedList method, we initialize the ArrayList variable with the ViewState. Then we loop through the GridView rows and check if any Checkbox is selected. If a Checkbox is selected, we again check the ArrayList to know if the particular checkbox is alreay contain in it. If unavailable, then we add it to the ArrayList collection. If the Checkbox is unselected, we do the reverse function. Finally, we store the ArrayList in the ViewState.

private void GetCheckedList()

{

  if (this.ViewState["ArrList"] != null) 

  { 

      arrlist = (ArrayList)this.ViewState["ArrList"]; 

  } 



  foreach (GridViewRow gvr in GridView1.Rows) 

  { 

    CheckBox chkSelect = (CheckBox)gvr.FindControl("chkSelect"); 

    if (chkSelect.Checked) 

    { 

        if (!arrlist.Contains(GridView1.DataKeys[gvr.RowIndex].Value.ToString())) 

          arrlist.Add(GridView1.DataKeys[gvr.RowIndex].Value.ToString()); 

    } 

    else 

    { 

        if (arrlist.Contains(GridView1.DataKeys[gvr.RowIndex].Value.ToString())) 

          arrlist.Remove(GridView1.DataKeys[gvr.RowIndex].Value.ToString()); 

    } 

   } 



  if (arrlist.Count > 0) 

  { 

    this.ViewState["ArrList"] = arrlist; 

  }

}

GridView RowDataBound Event

To retain the “selected” Checkboxes to keep selected, in the RowDataBound event of the GridView control, we check whether the ArrayList contains any values to be selected. If it contains the value, then we set the Checked property of the CheckBox to true;
{

  if (e.Row.RowType == DataControlRowType.DataRow) 

  { 

    if (arrlist.Contains(GridView1.DataKeys[e.Row.RowIndex].Value.ToString())) 

    { 

      CheckBox chkSelect = (CheckBox)e.Row.FindControl("chkSelect"); 

      chkSelect.Checked = true; 

    } 

  }

}

Now if you run this page in browser, you can see the GridView control populated with data and you can navigate through the pages. Select any checkbox in any page, navigate to other pages, then select some checkbox in that page, navigate as you like, you can see the checkbox you selected previously will be kept selected.

Export GridView To Excel

This is the main objective of this article. Exporting the data in the GridView control to an Excel sheet. To achieve this, we are going to create an instance (object) of GridView class. The GridView instance will have same column as the GridView control which displayed data in the page. With the help of RenderControl method of the GridView class, we will render the html of the GridView into a HtmlTextWriter object which in turn pass it to the StringWriter object. Then by using the FileStream, we create an Excel File, write the content in the StringWriter object into it. We define some MSO Number format for the decimal and date column in the Excel, and finally, we write a small piece of code to emit it in the client browser for Download. Let us go step by step to achieve this.


Step 1: Create GridView instance and bind it with a DataTable
BoundField CusCode = new BoundField();

BoundField CusName = new BoundField();

BoundField CusAge = new BoundField();

BoundField CusEmail = new BoundField();

BoundField SomeDecimals = new BoundField();

BoundField TodayDate = new BoundField();

ImageField ImgField = new ImageField();



CusCode.HeaderText = "Customer Code";

CusName.HeaderText = "Customer Name";

CusAge.HeaderText = "Age";

CusEmail.HeaderText = "Email Address";

SomeDecimals.HeaderText = "Decimals";

TodayDate.HeaderText = "Date";

ImgField.HeaderText = "Favorites";



CusCode.DataField = "Cus_Code";

CusName.DataField = "Cus_Name";

CusAge.DataField = "Cus_Age";

CusEmail.DataField = "Cus_Email";

SomeDecimals.DataField = "SomeDecimals";

TodayDate.DataField = "Today";

ImgField.DataImageUrlField = "ImgUrl";

ImgField.DataImageUrlFormatString = “YourSiteCompleteUrl” + "{0}";

ImgField.ItemStyle.Height = Unit.Pixel(100);

ImgField.ItemStyle.Width = Unit.Pixel(140);



GridView g = new GridView();

g.AutoGenerateColumns = false;



g.RowDataBound += new GridViewRowEventHandler(g_RowDataBound);



g.Columns.Insert(0, CusCode);

g.Columns.Insert(1, CusName);

g.Columns.Insert(2, CusAge);

g.Columns.Insert(3, CusEmail);

g.Columns.Insert(4, SomeDecimals);

g.Columns.Insert(5, TodayDate);

g.Columns.Insert(6, ImgField);



g.DataSource = dtData;

g.DataBind();

In the above code, we create six BoundField and one ImageField columns, set its HeaderText property. Then we specify the DataField for each column. Note that the last column is ImageField column, so we specify the DataImageUrlField and DataImageUrlFormatString.For DataImageUrlFormatString property, you have to specify the full URL of the Image location in the above specified format. Set the ImageField Height and Width. Then create the instance of GridView class, make its AutoGenerateColumns as false, then add the BoundField and ImageField columns in the GridView instance. Next we have to the bind the GridView instance with a DataTable object ‘dtData’ that contains the which has to be exported to Excel Sheet.
And the GridView instance is initialized with a RowDataBound event. The purpose of this event is to format some columns of the GridView control while rendering it. The RowDataBound event will be as follows.
protected void g_RowDataBound(object sender, GridViewRowEventArgs e)

{

  if (e.Row.RowType == DataControlRowType.DataRow) 

  { 

    e.Row.Cells[4].Attributes.Add("class", "text"); 

    e.Row.Cells[5].Attributes.Add("class", "dateformat"); 

  }

}

It is adding some class attributes to Cell[4] – Decimals and Cell[5] – Date columns. These classes will be defined in the following sections.

Step 2: Create StringWriter instance


StringWriter strwriter = new StringWriter();

HtmlTextWriter htmlwriter = new HtmlTextWriter(strwriter);

g.RenderControl(htmlwriter);

string htmlmarkup = strwriter.ToString();

The above code is easy to understand. We create a StringWriter and HtmlTextWriter instances and render the content of GridView instance as html into HtmlTextWriter instance which can be retrieved by the StringWriter instance. Now the complete Html source of the GridView instance will be in the variable called ‘htmlmarkup’.

Step 3: Create FileStream object

Then create an Excel file named as ‘File1.xls’, with FileStream object by using the string content in the htmlmarkup. By using the Write method of the FileStream, we write the htmlmarkup values into the Excel file. The code is given below.
FileStream fs = new FileStream(Server.MapPath("File1.xls"), FileMode.Create);

try

{

  Byte[] bContent = System.Text.Encoding.GetEncoding("utf-8").GetBytes(htmlmarkup);

  fs.Write(bContent, 0, bContent.Length);

}

catch (Exception ex)

{

  Response.Write(ex.Message);

}

finally

{

  fs.Close();

  fs.Dispose();

}

Step 4: Format Decimals and Date Column in Excel Sheet


We create the Cascading Style Sheet (CSS), in mso-number-format for the Decimal and DateTime column in the Excel Sheet. We frame the style classes as a string, then Response.Write it to the current page. Previously, in the GridView instances RowDataBound event, we have added these styles to the GridView instance cells 4 and 5, which is Decimal and Date respectively.

string style = @"<style> .text { mso-number-format:0\.00; } ";

style += ".dateformat {mso-number-format:\"Short Date\"}";

style += " </script>";



Response.Write(style);

Step 5: Popup Excel Sheet Download Dialog Box

The below code is reponsible to promt a download dialog in the client browser. Note that the attachment attribute of the AppendHeader is fixed with File1.xls. If you need to pass dynamic excel file name, you have pass your dynamic excel file name here.
Response.AppendHeader("content-disposition", "attachment;filename=File1.xls");


Response.WriteFile("File1.xls");

Response.End();

Complete Code for Export GridView To Excel:

By combining all the above block of codes, we create a method called ExportGridToExcel, which takes a DataTable argument. So by passing a DataTable object as parameter to this method, you can use this method anywhere and anytime in your page.
private void ExportGridToExcel(DataTable dtData)



  
StringWriter strwriter = new StringWriter(); 

  HtmlTextWriter htmlwriter = new HtmlTextWriter(strwriter); 

  BoundField CusCode = new BoundField(); 

  BoundField CusName = new BoundField(); 

  BoundField CusAge = new BoundField(); 

  BoundField CusEmail = new BoundField(); 

  BoundField SomeDecimals = new BoundField(); 

  BoundField TodayDate = new BoundField(); 

  ImageField ImgField = new ImageField(); 



  CusCode.HeaderText = "Customer Code"; 

  CusName.HeaderText = "Customer Name"; 

  CusAge.HeaderText = "Age"; 

  CusEmail.HeaderText = "Email Address"; 

  SomeDecimals.HeaderText = "Decimals"; 

  TodayDate.HeaderText = "Date"; 

  ImgField.HeaderText = "Favorites"; 

  ImgField.ItemStyle.Height = Unit.Pixel(100); 

  ImgField.ItemStyle.Width = Unit.Pixel(140); 



  CusCode.DataField = "Cus_Code"; 

  CusName.DataField = "Cus_Name"; 

  CusAge.DataField = "Cus_Age"; 

  CusEmail.DataField = "Cus_Email"; 

  SomeDecimals.DataField = "SomeDecimals"; 

  TodayDate.DataField = "Today"; 

  ImgField.DataImageUrlField = "ImgUrl"; 

  ImgField.DataImageUrlFormatString = “YourSiteUrl” + "{0}"; 



  GridView g = new GridView(); 

  g.AutoGenerateColumns = false; 

  g.RowDataBound += new GridViewRowEventHandler(g_RowDataBound); 



  g.Columns.Insert(0, CusCode); 

  g.Columns.Insert(1, CusName); 

  g.Columns.Insert(2, CusAge); 

  g.Columns.Insert(3, CusEmail); 

  g.Columns.Insert(4, SomeDecimals); 

  g.Columns.Insert(5, TodayDate); 

  g.Columns.Insert(6, ImgField); 

  g.DataSource = dtData; 

  g.DataBind(); 

  g.RenderControl(htmlwriter); 



  string htmlmarkup = strwriter.ToString(); 

  FileStream fs = new FileStream(Server.MapPath("File1.xls"), FileMode.Create); 



  try { 

    Byte[] bContent = System.Text.Encoding.GetEncoding("utf-8").GetBytes(htmlmarkup); 

    fs.Write(bContent, 0, bContent.Length); 

  } 



  catch (Exception ex) 

  { 

    Response.Write(ex.Message); 

  } 

  finally 

  { 

    fs.Close(); 

    fs.Dispose(); 

  } 



  string style = @"<style> .text { mso-number-format:0\.00; } "; 

  style += ".dateformat {mso-number-format:\"Short Date\"}"; 

  style += " </script>"; 

  Response.Write(style); 



  Response.AppendHeader("content-disposition", "attachment;filename=File1.xls"); 

  Response.WriteFile("File1.xls"); 

  Response.End();

}  

Export Complete GridView Data To Excel:

The purpose of first button is to Export All Rows in the GridView control to the Excel Sheet. In the click event of the Button1, write the following code.
protected void Button1_Click(object sender, EventArgs e)

{

  string sql = "Select Cus_Code, Cus_Name, Cus_Age, Cus_Email, SomeDecimals,
ImgUrl, Today

   from Customer Order By Cus_Name"; 

  SqlDataAdapter da = new SqlDataAdapter(sql, “YourConnectionString”); 

  DataTable dt = new DataTable(); 

  da.Fill(dt);

  ExportGridToExcel(dt);

}

The complete Customer records are in a DataTable object and by passing it to the ExportGridToExcel method, we can export the complete GridView rows to Excel.

Export ONLY Randomly Selected GridView Rows To Excel: 

The purpose of second button is to Export any rows that is selected randomly from any page of the GridView control to an Excel Sheet. In the click event of the Button2, write the following code.
protected void Button2_Click(object sender, EventArgs e)

{

  GetCheckedList(); 

  if (this.ViewState["ArrList"] != null) 

  { 

    arrlist = (ArrayList)this.ViewState["ArrList"]; 

  } 



  string sql = "Select Cus_Code, Cus_Name, Cus_Age, Cus_Email, SomeDecimals,
ImgUrl, Today from 

  Customer Order By Cus_Name"; 



  SqlDataAdapter da = new SqlDataAdapter(sql, “YourConnectionString”); 

  DataTable dt = new DataTable(); 

  da.Fill(dt); 

  DataView dv = dt.DefaultView; 

  DataTable dtTemp = dv.ToTable().Clone(); 



  for (int i = 0; i < arrlist.Count; i++) 

  { 

    dv.RowFilter = "Cus_Code=" + arrlist[i].ToString(); 

    dtTemp.ImportRow(dv.ToTable().Rows[0]); 

  } 



  ExportGridToExcel(dtTemp);



}

Call GetCheckedList method, to include “selected” checkbox values in the current GridView page with the previously selected values. The Customer information will be in a DataTable object dt. We define a DataView and create another Temp DataTable ‘dtTemp’ which is cloned from the DataTable dt. We loop through the ArrayList, filter the selected Customer and import it to the Temp DataTabe. Finally, we pass the Temp DataTable to the ExportGridToExcel method.

Export GridView Data From Selected Page To Excel:

The purpose of third button is to Export the selected Page of the GridView control to an Excel Sheet. In the Click event of Button3
protected void Button3_Click(object sender, EventArgs e)

{

  string sql = "Select Cus_Code, Cus_Name, Cus_Age, Cus_Email, SomeDecimals,
ImgUrl, Today   

  from Customer Order By Cus_Name"; 

  SqlDataAdapter da = new SqlDataAdapter(sql, “YourConnectionString”); 

  DataTable dt = new DataTable(); 

  da.Fill(dt); 



  DataView dv = dt.DefaultView; 

  DataTable dtTemp = dv.ToTable().Clone(); 



  foreach (GridViewRow gvr in GridView1.Rows) 

  { 

    dv.RowFilter = "Cus_Code=" + GridView1.DataKeys[gvr.RowIndex].Value.ToString(); 

    dtTemp.ImportRow(dv.ToTable().Rows[0]); 

  } 



  ExportGridToExcel(dtTemp);

}

The logic to export a selected GridView page to excel is same as exporting randomly selected rows to excel. But the main difference is at the looping. Here we loop through the GridView rows, not with the ArrayList. Looping through the GridView rows, with the DataKeys value, we filter the DataView, import row by row to the Temp DataTable and pass the Temp DataTable to ExportGridToExcel method.
There is another way to achieve this, by making the AllowPaging Property of the GridView instance to true and pass the selected PageIndex value of the GridView control to the ExportGridToExcel method as second parameter, and assign it PageIndex of the GridView instance. Bit of more logic required, which we don’t want to mess you too much. But you can always try it.

1 comment:

Suggestions are invited from readers