Friday, March 25, 2011

Send Text Messages (SMS) from your ASP pages, using http://www.2sms.com

Category: ASP, HTML, and XML
Type: Applications
Difficulty: Beginning

Version Compatibility:  Visual Basic 6  
More information: This code shows how to use the XML Interface provided by http://www.2sms.com to sedn text messages from within an ASP page using VBScript. This code requires a username from www.2sms.com - register now for a free trial account with no obligation. It also relies on the Microsoft XML Parser.
This code has been viewed 39906 times.
Instructions: Copy the declarations and code below and paste directly into your VB project.

Declarations:
Code:
<html>
  <HEAD>
    <BODY>

      <p>Sending XML <br>
       
    <% 
       
'========================================================
'This code requires a user account with http://www.2sms.com
'Register now for a free no obligation trial of the service
'Simply call this method with the username and password you
'choose on the site and include a message and destination
'
'REQUIRES: Microsoft's XML Parser
'========================================================


        'Insert your username and password (as used to log on to the website) here

            UserName = "USN"
            PassWord = "PWD"
        Message = "Testing SMS"
        Destination = "447788123123"
   

        'sample of how to retrieves the message and number from a previous posting page

            'Message = request.form("message")
            'Destination = request.form("number")



      
        'form up the first segment of XML

        strfirstpart= "<?xml version=" & Chr(34) & "1.0" & Chr(34) & "?> " & _
        "<Request xmlns:xsi=" & Chr(34) & "http://www.w3.org/2001/XMLSchema-instance" & Chr(34) & "

xsi:noNamespaceSchemaLocation=" & Chr(34) & "http://schema.2sms.com/1.0/0410_RequestSendMessage.xsd" & Chr(34) & " Version = " &

Chr(34) & "1.0" & Chr(34) & ">" & _
        "<Identification>" & _
        "<UserID><![CDATA[" & username & "]]></UserID>" & _
        "<Password>" & password & "</Password>" & _
        "</Identification>" & _
        "<Service>" & _
            "<ServiceName>SendMessage</ServiceName>" & _
            "<ServiceDetail>"

       
        '
        'Form up the middle of the XML. To send multiple messages within one document insert
        'multiple SingleMessage nodes (eg loop through the code below to add the strMidPart string
        'several times). Your document may contain up to 500 SingleMessage nodes.
        '
       
        strMidPart = strMidPart +"<SingleMessage><Destination>" & Destination & "</Destination><Text><![CDATA[" & Message &

"]]></Text></SingleMessage>"
       
        'form up the bottom of the xml document

        strEndPart =     "</ServiceDetail>" & _
                    "</Service>" & _
                "</Request>"

        'Concatonate all 3 strings to form complete xml document for sending
        XMLstring = strFirstPart + StrMidPart + strEndPart
       
    %>

    outgoing XML
    <textarea name="textfield" cols="50" rows="10"><%=XMLstring%>


Tuesday, March 22, 2011

Show grand total running total in GridView Footer

In many scenarios we need to display Running total as well as Grand total in GridView footer. In this post i will try to explain in an easy way that how we can display running total & Grand total in a GridView footer combindly. To explain this solution using an example here i use a sales order report. The report contains all sales order amount in a tabular way. I will use a GridView to display sales order amount and use GridView footer to display Running total & Grand total. Let we have a customer table with id and name column plus an orders table with OrderID,CustomerID,OrderDate & Amount. Now our goal is to show all customers order with amount as well as page wise running total & grand total. Note that running total is necessary when you enable paging in a GridView where as Grand total is always you can consider.

The output will be:

Fig: Page 1 runnig total

When you click on another page within GridView then you will get that page Running total with Grand total also.

Fig: Page 2 runnig total

To accomplish the above example please find the HTML markup code for your aspx page from below:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="gvgrandtotl.aspx.cs" Inherits="gvgrandtotl" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Gridview</title>
</head>

<body>

<form id="form1" runat="server">

<div>

<asp:GridView runat="server" ID="gvEdit" AllowPaging="true" PageSize="5" ShowFooter="true" OnPageIndexChanging="gvEdit_PageIndexChanging" OnRowDataBound="gvEdit_RowDataBound">
<Columns>
<asp:BoundField DataField="Code" HeaderText="Order Code">
</asp:BoundField>
<asp:BoundField DataField="Name" HeaderText="Name">
</asp:BoundField>

<asp:TemplateField HeaderText="Date" FooterStyle-BorderWidth="1px" FooterStyle-BorderColor="maroon">
<ItemTemplate>
<asp:Label ID="lblDate" runat="server" Text='<%# Convert.ToDateTime(Eval("Date")).ToString("dd-MM-yy")%>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:Label ID="lbltxtRTotal" runat="server">Running Total:</asp:Label><br />
<asp:Label ID="Label1" runat="server">Grand Total:</asp:Label><br />
</FooterTemplate>
</asp:TemplateField>

<asp:TemplateField HeaderText="Amount" FooterStyle-BorderWidth="1px" FooterStyle-BorderColor="maroon">
<ItemTemplate>
<asp:Label ID="lblAmount" runat="server" Text='<%# Eval("Amount").ToString()%>'></asp:Label>
</ItemTemplate>
<FooterTemplate>
<asp:Label ID="lblRTotal" runat="server"></asp:Label><br />
<asp:Label ID="lblGTotal" runat="server"></asp:Label>
</FooterTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>

</div>

</form>

</body>
</html>

To accomplish the above example please find the server side code for your aspx page from below:
using System;
using System.Data;
using System.Configuration;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data.SqlClient;

public partial class gvgrandtotl : System.Web.UI.Page
{


decimal RunningTotal = 0;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string connectionString = ConfigurationManager.ConnectionStrings["TestConnection"].ConnectionString;
DataTable dt = new DataTable();
SqlConnection conn = new SqlConnection(connectionString);
using (conn)
{
SqlDataAdapter ad = new SqlDataAdapter(
"SELECT OrderID Code,B.Name,OrderDate Date,Amount from "+
"orders A INNER JOIN customer B ON A.Customerid=B.ID", conn);
ad.Fill(dt);
}

decimal GrandTotal = 0;
foreach (DataRow oRow in dt.Rows)
GrandTotal += Convert.ToDecimal(oRow["Amount"]);

ViewState["GrandTotal"] = GrandTotal;

gvEdit.DataSource = dt;
gvEdit.DataBind();
ViewState["dt"] = dt;
}
}


protected void gvEdit_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
gvEdit.PageIndex = e.NewPageIndex;
gvEdit.DataSource = (DataTable)ViewState["dt"];
gvEdit.DataBind();
}


protected void gvEdit_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
RunningTotal += Convert.ToDecimal(DataBinder.Eval(e.Row.DataItem, "Amount"));

if (e.Row.RowType == DataControlRowType.Footer)
{
((Label)e.Row.FindControl("lblRTotal")).Text = RunningTotal.ToString();
((Label)e.Row.FindControl("lblGTotal")).Text = ViewState["GrandTotal"].ToString();
}
}
}


Code explanation:
If you look at the page load event where i bind the datasource to the GridView control then i use a viewstate variable to store the retrieved sql server data because i do not go to the database server again when user click on another page of the gridview. At that time i also calculate Grand total value from the datatable & stored it in viewstate which i use in GridView RowDataBound event.

When i assign the datasource property to the GridView control then the RowDataBound event automatically fired. In this event i need to check that does the DataControl Row Type is DataRow or not. If its a DataRow that means i found the DataItem object which will be currently rendered in our page. So by accumulating the amount value will give me the Running Total and Grnad total value we have calculated previously in page load event. So now check the DataControl Row Type for Footer row. If you find the footer row then you knew that in Fotter row we have placed two label named lblRTotal and lblGTotal. Now by using the FindControl method you can catch both labels. Now just put the running total in lblRTotal Text property and Grand total in lblGTotal label Text property. 

Monday, March 21, 2011

Asp.net gridview

Introduction

I have seen lot of beginners struggle with GridView and it is not working. To solve this to some extent, I have created an application with most of the events and properties of GridView. This may be helpful for getting a clear idea about working with GridView. This application also has a few JavaScript code snippets and stylesheets which may be useful to add some values to the GridView. Hope this article will be simple and sufficient.

What is a GridView?

The DataGrid or GridView control displays data in tabular form, and it also supports selecting, sorting, paging, and editing the data inside the grid itself. The DataGrid or GridView generates a BoundColumn for each field in the data source (AutoGenerateColumns=true). By directly assigning the data source to the GridView, the data can be rendered in separate columns, in the order it occurs in the data source. By default, the field names appear in the grid's column headers, and values are rendered in text labels. A default format is applied to non-string values and it can be changed.

GridView Fields

Column Name Description
BoundColumn To control the order and rendering of columns.
HyperLinkColumn Presents the bound data in HyperLink controls
ButtonColumn Bubbles a user command from within a row to the grid event handler
TemplateColumn Controls which controls are rendered in the column
CommandField Displays Edit, Update, and Cancel links in response to changes in the DataGrid control's EditItemIndex property.

BoundColumn

By explicitly creating a BoundColumn in the grid's Columns collection, the order and rendering of each column can be controlled. In the BoundField properties, when the DataField and the SortExpressions are given, sorting and rendering the data can be easily done.
bound.JPG

HyperLinkColumn

A HyperLinkColumn presents the bound data in HyperLink controls. This is typically used to navigate from an item in the grid to a Details view on another page by directly assigning the page URL in NavigationUrl or by rendering it from the database.
Hyperlink.JPG

TemplateColumn

With a TemplateColumn, the controls which are rendered in the column and the data fields bound to the controls can be controlled. By using the TemplateColumn, any type of data control can be inserted.
templete.JPG
By clicking the Edit Templates option, it opens the Template field where we can see controls that can be added like Label, CheckBox, and even a GridView.
templete1.JPG

CommandField

The EditCommandColumn is a special column type that supports in-place editing of the data in one row in the grid. EditCommandColumn interacts with another property of the grid: EditItemIndex. By default, the value of EditItemIndex is -1, meaning none of the rows (items) in the grid are being edited. If EditItemIndex is -1, an "edit" button is displayed in the EditCommandColumn for each of the rows in the grid.
When the "edit" button is clicked, the grid's EditCommand event is thrown. It's up to the programmer to handle this event in the code. The typical logic sets EditItemIndex to the selected row, and then rebinds the data to the grid.
When EditItemIndex is set to a particular row, the EditCommandColumn displays "update" and "cancel" buttons for that row ("edit" is still displayed for the other rows). These buttons cause the UpdateCommand and CancelCommand events to be thrown, respectively.
commanfield.JPG

Different Types of Events

Name Description
DataBinding Occurs when the server control binds to a data source. (Inherited from Control.)
DataBound Occurs after the server control binds to a data source. (Inherited from BaseDataBoundControl.)
Disposed Occurs when a server control is released from memory, which is the last stage of the server control lifecycle when an ASP.NET page is requested. (Inherited from Control.)
Init Occurs when the server control is initialized, which is the first step in its lifecycle. (Inherited from Control.)
Load Occurs when the server control is loaded into the Page object. (Inherited from Control.)
PageIndexChanged Occurs when one of the pager buttons is clicked, but after the GridView control handles the paging operation.
PageIndexChanging Occurs when one of the pager buttons is clicked, but before the GridView control handles the paging operation.
PreRender Occurs after the Control object is loaded but prior to rendering. (Inherited from Control.)
RowCancelingEdit Occurs when the Cancel button of a row in edit mode is clicked, but before the row exits edit mode.
RowCommand Occurs when a button is clicked in a GridView control.
RowCreated Occurs when a row is created in a GridView control.
RowDataBound Occurs when a data row is bound to data in a GridView control.
RowDeleted Occurs when a row's Delete button is clicked, but after the GridView control deletes the row.
RowDeleting Occurs when a row's Delete button is clicked, but before the GridView control deletes the row.
RowEditing Occurs when a row's Edit button is clicked, but before the GridView control enters edit mode.
RowUpdated Occurs when a row's Update button is clicked, but after the GridView control updates the row.
RowUpdating Occurs when a row's Update button is clicked, but before the GridView control updates the row.
SelectedIndexChanged Occurs when a row's Select button is clicked, but after the GridView control handles the select operation.
SelectedIndexChanging Occurs when a row's Select button is clicked, but before the GridView control handles the select operation.
Sorted Occurs when the hyperlink to sort a column is clicked, but after the GridView control handles the sort operation.
Sorting Occurs when the hyperlink to sort a column is clicked, but before the GridView control handles the sort operation.
Unload Occurs when the server control is unloaded from memory. (Inherited from Control.)
These events are all listed in the MSDN, and here I have given a few events which are frequently used in our day to day projects:

PageIndexChanging

This event occurs when the property of the grid AllowPaging is set to true, and in the code behind the PageIndexChanging event is fired.

Paging

The DataGrid provides the means to display a group of records from the data source (for example, the first 10), and then navigates to the "page" containing the next 10 records, and so on through the data.
Paging in DataGrid is enabled by setting AllowPaging to true. When enabled, the grid will display page navigation buttons either as "next/previous" buttons or as numeric buttons. When a page navigation button is clicked, the PageIndexChanged event is thrown. It's up to the programmer to handle this event in the code.
//AllowPaging="True"

//fires when the property AllowPaging is set to True
GridView2.PageIndex = e.NewPageIndex;
//Datasource method
TempTable();

RowCommand

The DataGrid fires the RowCommand event when any button is pressed. We can given any name as command name, and based on that, the check will be done and the loop will be executed.
//OnRowCommand="GridView6_RowCommand"
//CommandName="view" CommandArgument='<%# Bind("id") %>'

protected void GridView6_RowCommand(object sender, GridViewCommandEventArgs e)
{
  //this will check whether the command name is equal to view. If it is
  //equal to view the it will go inside the loop.
  if (e.CommandName == "view")
  {
    //to find which row is clicked
    int row = int.Parse(e.CommandArgument.ToString());
    StringBuilder sb = new StringBuilder();
    sb.Append("<script language="'javascript'">\r\n");
    //javascript to navigate to view page.
    sb.Append(" window.open('View.aspx?id=" + row + "',
        null,'width=350,height=300,scrollbars=yes,left=350,top=200,right=5,bottom=5');");
    sb.Append("</script>");
    if (!this.IsClientScriptBlockRegistered("BindScript"))
    {
      RegisterClientScriptBlock("openScript", sb.ToString());
    }
  }
}

RowCreated

The DataGrid fires the RowCreate event when a new row is created.
//OnRowCreated="GridView3_RowCreated" 

protected void GridView3_RowCreated(object sender, GridViewRowEventArgs e)
{
  //When a child checkbox is unchecked then the header checkbox will also be unchecked.
  if (e.Row.RowType == DataControlRowType.DataRow && (e.Row.RowState == 
    DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate))
  {
    CheckBox chkBxSelect = (CheckBox)e.Row.Cells[1].FindControl("chkselect");
    CheckBox chkBxHeader = (CheckBox)this.GridView3.HeaderRow.FindControl("chkHeader");
    chkBxSelect.Attributes["onclick"] = string.Format("javascript:ChildClick(
        this,'{0}');", chkBxHeader.ClientID);
  }
}

RowDeleting

The DataGrid fires the RowDeleting event when the command name is given as Delete.
//OnRowDeleting="GridView3_RowDeleting"
//CommandName="delete"

protected void GridView3_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
  //finding the row index of which row is selected.
  int row = e.RowIndex;
  //deleteing the temp row(or your delete query that will delete the row from
  //the database
  Temp.Rows[row].Delete();
  //reassigning the datasource
  GridView3.DataSource = Temp;
  GridView3.DataBind();
}

RowUpdating

The DataGrid fires the RowUpdating event when the command name is given as Update.
//OnRowUpdating="GridView3_RowUpdating"

CommandName="update"
protected void GridView3_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
  //maintaining the row index to intial position
  GridView3.EditIndex = e.RowIndex;
  //reassigning the datasource
  GridView3.DataSource = Temp;
  GridView3.DataBind();
}

RowEditing

The DataGrid fires the RowEditing event when the command name is given as Edit.
/OnRowEditing="GridView3_RowEditing"
//CommandName="edit"

protected void GridView3_RowEditing(object sender, GridViewEditEventArgs e)
{
  //changing the index of the grid.
  GridView3.EditIndex = e.NewEditIndex - 1;
  //finding the row of the grid
  int row = e.NewEditIndex;
  //updating the rows inside the grid
  Temp.Rows[row]["name"] = ((TextBox)GridView3.Rows[e.NewEditIndex].FindControl(
      "txtname")).Text;
  Temp.Rows[row]["gender"] = ((DropDownList)GridView3.Rows[e.NewEditIndex].FindControl(
      "ddlgender")).Text;
  Temp.Rows[row]["email"] = ((TextBox)GridView3.Rows[e.NewEditIndex].FindControl(
      "txtemail")).Text;
  Temp.Rows[row]["salary"] = ((TextBox)GridView3.Rows[e.NewEditIndex].FindControl(
      "txtsalary")).Text;
  Temp.AcceptChanges();
  //total of the salary and displaying it in footer.
  Total = decimal.Parse(Temp.Compute("sum(Salary)", "Salary>=0").ToString());
  //maintaining the total value in the session
  Session["Salary"] = Total;
  //reassigning the datasource
  GridView3.DataSource = Temp;
  GridView3.DataBind();
}

RowDatabound

The DataGrid fires the RowDatabound event when a data row is bound to data in a GridView control.
//OnRowDataBound="GridView4_RowDataBound"

protected void GridView4_RowDataBound(object sender, GridViewRowEventArgs e)
{
  //Example for grid inside a grid
  //to find the row index to the selected row.
  int index = e.Row.RowIndex;
  //check whether the row type is equal
  if (e.Row.RowType == DataControlRowType.DataRow)
  {
    //assiging datasource for the grid inside the grid.
    DataTable tempdt = new DataTable();
    tempdt.Columns.Add("Id");
    tempdt.Columns.Add("Language", typeof(String));
    DataRow dr = tempdt.NewRow();
    dr["Id"] = 1;
    dr["Language"] = "C";
    tempdt.Rows.Add(dr);
    DataRow dr1 = tempdt.NewRow();
    dr1["Id"] = 2;
    dr1["Language"] = "C++";
    tempdt.Rows.Add(dr1);
    GridView gr = (GridView)(e.Row.FindControl("GridView5"));
    if (gr != null)
    {
      gr.DataSource = tempdt;
      gr.DataBind();
    }
  }
}

Sorting

Data in a grid is commonly sorted by clicking the header of the column to sort. Sorting in a DataGrid can be enabled by setting AllowSorting to true. When enabled, the grid renders LinkButton controls in the header for each column. When the button is clicked, the grid's SortCommand event is thrown. It's up to the programmer to handle this event in the code. Because DataGrid always displays the data in the same order it occurs in the data source, the typical logic sorts the data source, and then rebinds the data to the grid.
//AllowSorting="True"
//OnSorting="GridView2_Sorting"
//SortExpression="name"

protected void GridView2_Sorting(object sender, GridViewSortEventArgs e)
{
  //to check whether to display in ascending order or descending order
  if (e.SortExpression.Trim() == this.SortField)
    this.SortDirection = (this.SortDirection == "D" ? "A" : "D");
  else
    this.SortDirection = "A";
  this.SortField = e.SortExpression;
  TempTable();
}

Header and Footer of the Grid

This is a JavaScript to use when there is a necessity to check all the checkboxes in a column by checking a single checkbox in the grid header. While we are checking the grid header, all the checkboxes inside the grid will be selected, and if any child column is unchecked, then the header will also be unchecked.
<script type="text/javascript">
var TotalChkBx;
var Counter;
window.onload = function()
{
    TotalChkBx = parseInt('<%= this.GridView3.Rows.Count %>');
    Counter = 0;
}
function SelectAll(CheckBox)
{
    var TargetBaseControl = document.getElementById('<%= this.GridView3.ClientID %>');
    var TargetChildControl = "chkselect";
    var Inputs = TargetBaseControl.getElementsByTagName("input");
    for(var n = 0; n < Inputs.length; ++n)
    if(Inputs[n].type == 'checkbox' && Inputs[n].id.indexOf(TargetChildControl,0) >= 0)
    Inputs[n].checked = CheckBox.checked;
    Counter = CheckBox.checked ? TotalChkBx : 0;
}
function ChildClick(CheckBox, HCheckBox)
{
    var HeaderCheckBox = document.getElementById(HCheckBox);
    if(CheckBox.checked && Counter < TotalChkBx)
    Counter++;
    else if(Counter > 0)
    Counter--;
    if(Counter < TotalChkBx)
    HeaderCheckBox.checked = false;
    else if(Counter == TotalChkBx)
    HeaderCheckBox.checked = true;
}
</script>
onclick="javascript:SelectAll(this);"
Maintaining the values in the footer is done by the following code:
protected void GridView3_RowDataBound(object sender, GridViewRowEventArgs e)
{
  //total in the footer
  if (e.Row.RowType == DataControlRowType.Footer)
  {
    e.Row.Cells[4].Text = this.Total.ToString();
  }
}
To maintain the grid header in a fixed state, a style sheet is used in the source code:
<style type="text/css">.DataGridFixedHeader { 
BACKGROUND: url(Images/grid_header.jpg) repeat-x; 
POSITION: relative; TOP: expression(this.offsetParent.scrollTop); HEIGHT: 27px }

</style>
<div style="width:80%; height:250px; overflow:auto;" >
<asp:GridView ID="GridView1" runat="server" 
    Width="100%" AutoGenerateColumns="False" 
    CellPadding="4" DataKeyNames="id" 
    ForeColor="#333333" GridLines="None"
    OnRowCreated="GridView1_RowCreated" 
    AllowPaging="True" AllowSorting="True"
    OnPageIndexChanging="GridView1_PageIndexChanging" OnSorting="GridView1_Sorting"
    PageSize="2" OnRowCommand="GridView1_RowCommand"
    OnRowDeleting="GridView1_RowDeleting" 
    OnRowEditing="GridView1_RowEditing">
....
....
<HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White"
    CssClass="gridHeader DataGridFixedHeader" />
.....
.....
</asp:GridView>
</div>

Export GridView to Word, Excel, and PDF

Word

protected void btnword_Click(object sender, EventArgs e)
{
    Response.AddHeader("content-disposition", 
                       "attachment;filename=Information.doc");
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.ContentType = "application/vnd.word";
    System.IO.StringWriter stringWrite = new System.IO.StringWriter();
    System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
    // Create a form to contain the grid
    HtmlForm frm = new HtmlForm();
    GridView1.Parent.Controls.Add(frm);
    frm.Attributes["runat"] = "server";
    frm.Controls.Add(GridView1);
    frm.RenderControl(htmlWrite);
    //GridView1.RenderControl(htw);
    Response.Write(stringWrite.ToString());
    Response.End();
}

Excel

protected void btnexcel_Click(object sender, EventArgs e)
{
    string attachment = "attachment; filename=Information.xls";
    Response.ClearContent();
    Response.AddHeader("content-disposition", attachment);
    Response.ContentType = "application/ms-excel";
    StringWriter sw = new StringWriter();
    HtmlTextWriter htw = new HtmlTextWriter(sw);
    // Create a form to contain the grid
    HtmlForm frm = new HtmlForm();
    GridView1.Parent.Controls.Add(frm);
    frm.Attributes["runat"] = "server";
    frm.Controls.Add(GridView1);
    frm.RenderControl(htw);
    //GridView1.RenderControl(htw);
    Response.Write(sw.ToString());
    Response.End();
}

PDF

protected void btnpdf_Click(object sender, EventArgs e)
{
    Response.Clear();
    StringWriter sw = new StringWriter();
    HtmlTextWriter htw = new HtmlTextWriter(sw);
    GridView1.RenderControl(htw);
    Response.ContentType = "application/pdf";
    Response.AddHeader("content-disposition", 
             "attachment; filename=Information.pdf");
    Response.Write(sw.ToString());
    Response.End();
}

Difference between DataGrid, DataList, and Repeater

A DataGrid, DataList, and Repeater are all ASP.NET data Web controls.
They have many things in common like the DataSource property, the DataBind method, and the ItemDataBound and ItemCreated events.
When you assign the DataSource property of a DataGrid to a DataSet, each DataRow present in the DataRow collection of the DataTable is assigned to a corresponding DataGridItem, and this is the same for the rest of the two controls. But The HTML code generated for a DataGrid has an HTML TABLE <ROW> element created for the particular DataRow and it is a table form representation with columns and rows. For a DataList, it is an array of rows, and based on the template selected and the RepeatColumn property value, we can specify how many DataSource records should appear per HTML <table> row. In short, in a DataGrid, we have one record per row, but in a DataList, we can have five or six rows per row. For a Repeater control, the data records to be displayed depend upon the templates specified, and the only HTML generated is the due to the templates.
In addition to these, DataGrid has in-built support for sorting, filtering, and paging the data, which is not possible when using a DataList, and for a Repeater control, we would require to write explicit code to do paging.

Thursday, March 10, 2011

Generating XP style webparts from a web control

Introduction
The Internet is a tremendously important resource professionally and for private use, yet organizing access to frequently visited sites is left to the Favorites menu. For some reason Favorites doesn't work for me. It's great for filing away and organizing bookmarks to pages of interest but, for me, its not visible enough. The Links toolbar is good for a few items, but like many users, I visit a core set of about 30 sites and this is too many for the Links toolbar. So to try and address my own requirements, I've come up with my own home page that might also work for you. The page uses a web control that serves as an illustration of an ASP.NET control in action and that you might find a use for elsewhere.
As you can see from the image, the control is included in an ASP.NET (.aspx) page and comprises one or more group boxes. In this case the web part is one that Microsoft previously used on their MSDN site for presenting current information, grouped by topic - just as I want to have happen. The group boxes are expandable, they remember their collapsed/expanded state (even between successive IE sessions), so that the information on view can be restricted, but always show the header, so I know what's available and can be moved around. So far so good, but creating a web part using HTML is, while not not challenging, tedious because the tables have to be laid out just-so and use specific style classes recognized by the web part. Also, having the web part implemented in HTML means that changes are not straight forward as there is no separation of code and content. Sounds like an ideal candidate for being rendered by a web control.

The web page

Allowing the web parts to be generated automatically simplifies the process of creating a page and allows the source of the data to be a database or XML file. In my case I've chosen to use an XML file but more about that in a moment. Here is the HTML for the web page shown above:
Collapse
<%@ Page Language="vb" AutoEventWireup="false" 
         Inherits="System.Web.UI.Page" %>
 <%@ Register TagPrefix="Links" 
         Namespace="Lyquidity.UtilityLibrary.WebControls" 
         Assembly = "Lyquidity.UtilityLibrary.WebControls" %>
 <HTML>
  <HEAD>
   <style> BODY { font-family:verdana,arial,helvetica; margin:0; }</style>
  </HEAD>
  <body onload="preloadImages();">
   <table cellpadding="0" cellspacing="0" width="100%" height="100%" 
                                                       border="0">
    <tr>
     <!-- Column for web gadgets/parts -->
     <td width="150" valign="top">
      <Links:WebPartsControl id="LinksWebParts" 
               LinkFrameName="LinkPage" 
               DefaultPage="http://www.my_fave_page.com" runat="server" />
     </td>
     <td width="20"> </td>
     <!-- Column to display default or frame link page -->
     <td width="100%">
      <iframe id="LinkFrame" name="LinkPage" width="100%" height="100%" 
        frameborder="0" scrolling="auto" 
        src="<%=LinksWebParts.DefaultPage%>">
      </iframe>
     </td>
    </tr>
   </table>
  </body>
 </HTML>
Not much to it. The key line is the one containing the <Links:WebPartsControl> tag. The tag is registered as required and the tag reference (<Links:WebPartsControl ... >) initializes the WebPartControl class that, in turn, presents the required group boxes. Note that the page is inherited directly from System.Web.UI.Page and does not use a code-behind implementation. This is just to remove all obfuscation from the example. The disadvantage of this page as presented is that it cannot be used in the designer.
The web part sits within a table comprising 3 columns. The positioning of the web part is controlled exclusively by this "outer" HTML. In this case, it sits within the left hand column. The middle column is used to provide a gap between it and the default page that is displayed on the right. The right hand column contains a frame, the source for which is a default page. You could just type in the name of a page to use, but the WebPartsControl provides a default to use. The default is initialized within the control to http://www.google.com (that, of course, you can change). But you can override this default by specifying one in the tag definition (see the HTML) or by using a parameter on the page reference (see below).

The XML file

The number of web parts and the links to be hosted by any given web part is defined by an associated xml file. Here is an example:
Collapse
<?xml version="1.0" encoding="utf-8" ?>
 <links> 
  <framelinks title="Code Pages" id="BE96D11D-326D-4910-A837-84D658DDF024">
   
   <link name="Code Project" 
         page="&lt;A href=&quot;http://www.codeproject.com/&quot;&gt;
         http://www.codeproject.com&lt;/A&gt;" alttext="Code Project" />
   <link name="Programmers Heaven" 
          page="&lt;A href=&quot;http://www.programmersheaven.com/&quot;&gt;
          http://www.programmersheaven.com&lt;/A&gt;" 
          alttext="Programmers Heaven" />
   <link name="Planet Source Code" 
          page="&lt;A href=&quot;http://www.planetsourcecode.com/&quot;&gt;
          http://www.planetsourcecode.com&lt;/A&gt;" 
          alttext="Planet Source Code" />
   <link name="Got Dot Net" 
          page="&lt;A href=&quot;http://www.gotdotnet.com/&quot;&gt;
          http://www.gotdotnet.com&lt;/A&gt;" alttext="Got Dot Net" />
   <link name="MSDN" page="&lt;A href=&quot;http://msdn.microsoft.com/&quot;&gt;
          http://msdn.microsoft.com&lt;/A&gt;" 
          alttext="Microsoft Developer Network" />
   <link name="24 / 7" page="&lt;A href=&quot;http://www.net247.com/&quot;&gt;
          http://www.net247.com&lt;/A&gt;" 
          alttext="Microsoft Developer Network" /> 
  </framelinks> 
  <popuplinks title="Links" id="E812F25B-3099-412e-A7E2-EA8FC397D169"> 
   <group name="Search Engines"> 
    <link name="Google" url="&lt;A href=&quot;http://www.google.co.uk/&quot;&gt;
          http://www.google.co.uk/&lt;/A&gt;" alttext="UK Google" />
    <link name="Yahoo" url="&lt;A href=&quot;http://www.yahoo.co.uk/&quot;&gt;
          http://www.yahoo.co.uk/&lt;/A&gt;" /> 
   </group> 
   <group name="Legal"> 
    <link name="Inland Revenue FAQ" 
          url="&lt;A href=&quot;http://www.inlandrevenue.gov.uk/&quot;&gt;
          http://www.inlandrevenue.gov.uk&lt;/A&gt;" />
    <link name="VAT" 
          url="&lt;A href=&quot;http://www.hmce.gov.uk/business/vat/vat.htm&quot;&gt;
          http://www.hmce.gov.uk/business/vat/vat.htm&lt;/A&gt;" />
    <link name="Companies House" 
          url="&lt;A href=&quot;http://www.companies-house.co.uk/&quot;&gt;
          http://www.companies-house.co.uk&lt;/A&gt;" /> 
   </group> 
  </popuplinks> 
  <popuplinks title="Other" id="E912F25B-3099-412e-A7E2-EA8FC397D169"> 
   <group name="Map Info"> 
    <link name="Street Map" 
          url="&lt;A href=&quot;http://www.streetmap.co.uk/&quot;&gt;
          http://www.streetmap.co.uk&lt;/A&gt;" />
   
   </group> 
   <group name="ISP"> 
    <link name="BlueYonder Status" 
          url="&lt;A href=&quot;http://status.blueyonder.co.uk:888/&quot;&gt;
          http://status.blueyonder.co.uk:888/&lt;/A&gt;" />
    <link name="BlueYonder Selfcare" 
          url="&lt;A href=&quot;http://selfcare.blueyonder.co.uk/&quot;&gt;
          http://selfcare.blueyonder.co.uk/&lt;/A&gt;" /> 
   </group> 
  </popuplinks> 
 </links>
There are two type of links: "frame" and "popup". A frame link will cause the referenced page to be displayed in a named frame (which defaults to _Parent), while a popup will always display the page in the parents frame. The example use of the web control given above, specifies that the frame named LinkPage is to be used. There are two other significant differences between frame and popup link types: there can be only one FrameLink entry; popup links can have groups. The XML sample shown illustrates all tags and attributes recognized by the web control, so a moments review will provide almost all the information needed. The ID attribute is worth an additional comment. The web part will store its expanded/collapsed state and order so that it can be retrieved then next time IE is used. However it can only restore order if the parts being used have unique IDs. I've used GUIDs but any other unique ID will do. For example "A", "B", "C"...

The web part

The web part control is based on a web part implementation like the one that used to feature on the MSDN site. A web part is a component comprising some HTML and Java Script. Unlike a component in .NET, the presentation (HTML) is not in the same place as the code. But like a .NET component, the web part code encapsulates the functionality of the component. The link between HTML and code is a style class. In IE, a style class can have its behavior attribute set to refer to a file containing the code and a <component> tag. The following diagram attempts to depict these relationships:
Collapse
<element 
    class="MyComponentStyle">
              |
              V
        MyComponentStyle 
        { behaviour:url(MyComponentCode.xxx); }
                           |
                           V
    <PUBLIC:COMPONENT> 
     <PUBLIC:ATTACH EVENT="oncontentready"   ONEVENT="fnInit()"   />
     <PUBLIC:ATTACH EVENT="onmousedown" ONEVENT="fnGrab()"   />
     <PUBLIC:ATTACH EVENT="ondragstart" ONEVENT="fnCancel()" /> 
     <SCRIPT LANGUAGE="JavaScript"> 
      function fnInit()
      {
      ....
      } 
      function fnGrab()
      {
      ....
      } 
      function fnCancel()
      {
      ....
      } 
     </SCRIPT> 
    </PUBLIC:COMPONENT>
The code for the web part component used in this control can be found in the webparts.htc file in the webparts folder. The styles used by the web part, which includes the one that links the web part HTML to web part code, can be found in the file ie.css again in the webparts folder.
The HTML used for the web parts can be found in both the Render() method of the web control and within files in the HTML folder. This folder contains four files. Each contains a snippet of the HTML used to show the collapsible group boxes. The HTML is broken up into snippets so that they can be reused easily by the web control. They are held in files so that they can, if needed, be modified without the need to recompile the web control.
The first file to review is webpart.txt the contents of which are reproduced here:
Collapse
<!-- Web part Start -->
 <table cellpadding="0" cellspacing="0" class="clsPart" 
            width="150" border="0" id="%id">
  <tr>
   <td class="clsPartHead" valign="top" align="left" height="19" width="15">
    <img class="clsPartHead" src="webparts/images/gripblue.gif" 
            height="19" width="15">
   </td>
   <td class="clsPartHead" valign="middle" align="left" width="150">
    <b class="clsPartHead">%WebPartTitle</b>
   </td>
   <td class="clsPartRight" valign="top" align="right" 
            height="19" width="25">
    <img class="clsMinimize" src="webparts/images/downlevel.gif" 
            height="19" width="25">
   </td>
  </tr>
  <tr>
   <td colspan="3">
    <table bgcolor="#ffffff" width="100%" cellpadding="0" 
            cellspacing="0" border="0">
     <tr>
      <td bgcolor="#6699cc" colspan="1" width="1px" 
           valign="top"><div 
           style="margin:1px;padding:0px;"></div>
      </td>
      <td width="150" colspan="2" bgcolor="#f1f1f1" valign="top">
       <table width="150" cellpadding="0" cellspacing="0" border="0">
        <tr>
         <td colspan="2" height="10"></td>
        </tr>               
        %Elements               
       </table>
      </td>
      <td bgcolor="#6699cc" colspan="1" width="1px" 
            valign="top"><div 
            style="margin:1px;padding:0px;"></div>
      </td>
     </tr>
     <tr>
      <td bgcolor="#6699cc" colspan="3" height="1" 
            valign="top"><div 
            style="margin:1px;padding:0px;"></div>
      </td>
     </tr>
    </table>
   </td>
  </tr>
 </table>
 <!-- Web part end -->
This HTML defines the table that controls the layout of a group box, the position of the button and the grab handle, its title and where the content will be located. This snippet contains the identifiers %WebPartTitle and %Elements. The web control will substitute these identifiers with HTML (based on HTML snippets from other files) to create a complete group box that is returned to the user's browser.
You will see that the main table of this snippet is assigned a style class. As well as providing format styling, the use of this class allows the associated component code, residing in the .htc file, to identify the HTML elements that are part of the component. In this case the class is called clsPart.

The WebParts control

The entire web part is a collection of any number of the group boxes. This list of group boxes are, in turn, held within a containing table. The HTML for this table can be found in the Render() method of the web control:
Collapse
<table cellpadding='0' cellspacing='0' 
  width='150' id='topTable' class='clsPartContainer'
  style='MARGIN-LEFT:10px; MARGIN-RIGHT:10px' border='0'> 
Critical to the web part is the use of the style class clsPartContainer. It is this class, defined in webparts/ie.css, that has its behaviour bound to the code in webparts.htc.
The control also links in the required style sheets, but its main task is to generate the HTML required to present to the user, the links defined in the XML file.
Sample Image - links.jpg

Properties

The control presents four properties:

LinkFrameName

The name of the frame in which pages referenced in <framelink> links should be displayed. By default this is _Parent in which case the behavior of popup link and frame link types is identical.

HTML

The name of the web folder containing the web part snippets. By default this is HTML, a folder located beneath the containing page, for example:
Collapse
<A href="http://www.mysite.com/linkpages/html">
         http://www.mysite.com/linkpages/html</A>

DefaultPage

This defines the page that will be displayed by default (if required) and defaults to
Collapse
<A href="http://www.google.com">http://www.google.com</A>
The value of the DefaultPage property can be modified in two ways (without changing the web control code):
  1. By including a value for the DefaultPage property when the control is defined on the containing web page:
    Collapse
    <Links:WebPartsControl id="LinksWebParts" 
             LinkFrameName="LinkPage" 
             DefaultPage="&lt;A href=&quot;http://www.my_fave_page.com/&quot;&gt;
             http://www.my_fave_page.com&lt;/A&gt;" 
             runat="server" /> 
    In this example, the default page displayed in the the <IFrame> will be
    Collapse
    <A href="http://www.my_fave_page.com">http://www.my_fave_page.com</A>
  2. By including a parameter in the address:
    Collapse
    <A href="http://localhost/links.aspx?DefaultPage=
       http://www.my_fave_page.com">
       http://localhost/links.aspx?DefaultPage=
       http://www.my_fave_page.com</A>

XMLFile

This defines the name of the XML file used to contain the link information. The default is links.xml. Like the DefaultPage, this property can be modified by including a value when the control is defined on the containing web page:
Collapse
<Links:WebPartsControl id="LinksWebParts" 
           LinkFrameName="LinkPage" 
           XMLFile="BMSLinks.xml" runat="server" /> 
or by including a parameter in the address:
Collapse
<A href="http://localhost/links.aspx?XMLFile=BMSLinks.xml">
   http://localhost/links.aspx?XMLFile=BMSLinks.xml</A>

Things that could improve the control

  • Add the option to include icon by links
  • Provide a facility to hide the control (like a toolbox)
  • Add ability for setting width property (this is difficult because some of the width determinants are in style classes and within the .htc file)
  • Add a colour property (this far from impossible but yet more difficult as the colour is intrinsic to the images used)