26 May 2014

Build Master Detail GridView using jQuery in ASP.Net

In this article I will explain how to build Master / Detail GridView using jQuery in ASP.Net.
The detail GridView can be expanded and collapsed using jQuery when button is clicked in the Master GridView.
Nested GridViews Example i.e. GridView inside GridView with Expand Collapse


Database
I’ll make use of Customers and Orders Table of Microsoft’s Northwind Database which you can easily download using the link provided below
 
HTML Markup
The HTML Markup contains a simple ASP.Net GridView with a child ASP.Net GridView in the ItemTemplate of TemplateField of ASP.Net GridView
<asp:GridView ID="gvCustomers" runat="server" AutoGenerateColumns="false" CssClass="Grid"
    DataKeyNames="CustomerID" OnRowDataBound="OnRowDataBound">
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <img alt = "" style="cursor: pointer" src="images/plus.png" />
                <asp:Panel ID="pnlOrders" runat="server" Style="display: none">
                    <asp:GridView ID="gvOrders" runat="server" AutoGenerateColumns="false" CssClass = "ChildGrid">
                        <Columns>
                            <asp:BoundField ItemStyle-Width="150px" DataField="OrderId" HeaderText="Order Id" />
                            <asp:BoundField ItemStyle-Width="150px" DataField="OrderDate" HeaderText="Date" />
                        </Columns>
                    </asp:GridView>
                </asp:Panel>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:BoundField ItemStyle-Width="150px" DataField="ContactName" HeaderText="Contact Name" />
        <asp:BoundField ItemStyle-Width="150px" DataField="City" HeaderText="City" />
    </Columns>
</asp:GridView>
 
 
Binding the Customers records to the Parent GridView
Below is the code to bind the parent ASP.Net GridView with the records of Customers table from the Northwind Database.
C#
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        gvCustomers.DataSource = GetData("select top 10 * from Customers");
        gvCustomers.DataBind();
    }
}
 
private static DataTable GetData(string query)
{
    string strConnString = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
    using (SqlConnection con = new SqlConnection(strConnString))
    {
        using (SqlCommand cmd = new SqlCommand())
        {
            cmd.CommandText = query;
            using (SqlDataAdapter sda = new SqlDataAdapter())
            {
                cmd.Connection = con;
                sda.SelectCommand = cmd;
                using (DataSet ds = new DataSet())
                {
                    DataTable dt = new DataTable();
                    sda.Fill(dt);
                    return dt;
                }
            }
        }
    }
}
 
VB.Net
Protected Sub Page_Load(sender As Object, e As EventArgsHandles Me.Load
    If Not IsPostBack Then
        gvCustomers.DataSource = GetData("select top 10 * from Customers")
        gvCustomers.DataBind()
    End If
End Sub
 
Private Shared Function GetData(query As StringAs DataTable
    Dim strConnString As String = ConfigurationManager.ConnectionStrings("constr").ConnectionString
    Using con As New SqlConnection(strConnString)
        Using cmd As New SqlCommand()
            cmd.CommandText = query
            Using sda As New SqlDataAdapter()
                cmd.Connection = con
                sda.SelectCommand = cmd
                Using ds As New DataSet()
                    Dim dt As New DataTable()
                    sda.Fill(dt)
                    Return dt
                End Using
            End Using
        End Using
    End Using
End Function
 
 
Binding the Child GridView with the Orders for each Customer in the Parent GridView
On the RowDataBound event of the Parent GridView I am first searching the Child GridView in the corresponding GridView Row and then populating it with the records from the Orders table of the Northwind Database based on Customer Id stored in the DataKey property.
Note:GetData is a generic function and the same function discussed above is used here.
C#
protected void OnRowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        string customerId = gvCustomers.DataKeys[e.Row.RowIndex].Value.ToString();
        GridView gvOrders = e.Row.FindControl("gvOrders"as GridView;
        gvOrders.DataSource = GetData(string.Format("select top 3 * from Orders where CustomerId='{0}'", customerId));
        gvOrders.DataBind();
    }
}
 
VB.Net
Protected Sub OnRowDataBound(sender As Object, e As GridViewRowEventArgs)
    If e.Row.RowType = DataControlRowType.DataRow Then
        Dim customerId As String = gvCustomers.DataKeys(e.Row.RowIndex).Value.ToString()
        Dim gvOrders As GridView = TryCast(e.Row.FindControl("gvOrders"), GridView)
        gvOrders.DataSource = GetData(String.Format("select top 3 * from Orders where CustomerId='{0}'", customerId))
        gvOrders.DataBind()
    End If
End Sub
 
 
Client side Expand Collapse functionality using jQuery and JavaScript
For Expand and Collapse of the Child GridViews I have made use of jQuery
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript">
    $("[src*=plus]").live("click"function () {
        $(this).closest("tr").after("<tr><td></td><td colspan = '999'>" + $(this).next().html() + "</td></tr>")
        $(this).attr("src""images/minus.png");
    });
    $("[src*=minus]").live("click"function () {
        $(this).attr("src""images/plus.png");
        $(this).closest("tr").next().remove();
    });
</script>

Dynamically change the error message of ASP.Net CustomValidator using JavaScript


In this article I will explain how to dynamically change the error message of ASP.Net Custom Validator using JavaScript.
In order to illustrate the functioning with an example, I have created a simple form where a person has to enter either Phone Number or Cell Number and based on what he chooses the validation message of the ASP.Net CustomValidator is dynamically changed using JavaScript.
 
HTML Markup
In the below HTML Markup I have a RadioButtonList to allow user choose whether he has to enter Phone Number or Cell Number, and there is a TextBox for each of  the respective Phone and Cell Number and a CustomValidator for which I have set the ErrorMessage property as blank string.
<table cellpadding="2" cellspacing="2">
<tr>
    <td>
        <asp:RadioButton ID="rbPhoneNumber" Text="Phone Number" runat="server" GroupName="ContactNumber" />
    </td>
    <td>
        <asp:TextBox ID="txtPhoneNumber" runat="server" />
    </td>
</tr>
<tr>
    <td>
        <asp:RadioButton ID="rbCellNumber" Text="Cell Number" runat="server" GroupName="ContactNumber" />
    </td>
    <td>
        <asp:TextBox ID="txtCellNumber" runat="server" />
    </td>
</tr>
<tr>
    <td colspan="2">
        <asp:CustomValidator runat="server" Display="Dynamic"
            ForeColor="Red" ClientValidationFunction="ContactNumberValidation" ErrorMessage=""></asp:CustomValidator>
    </td>
</tr>
<tr>
    <td>
    </td>
    <td>
        <asp:Button ID="btnSave" runat="server" Text="Save" />
    </td>
</tr>
</table>
 
 
Dynamically changing the error message of ASP.Net Custom Validator using JavaScript
Below is the client side JavaScript validation function which is executed by the CustomValidator.
Note: The CustomValidator is rendered as HTML SPAN and hence I am making use of innerHTML property to set the error message.
Initially I am setting a generic error message which will be raised when user has not chosen the phone number type from the RadioButtonList.
Then based on what he chooses i.e. Phone number or Cell number he will see the respective error message when he leaves the corresponding TextBox empty.
<script type="text/javascript">
function ContactNumberValidation(sender, args) {
    sender.innerHTML = "Please select at least one contact number.";
    if (document.getElementById("<%=rbPhoneNumber.ClientID %>").checked) {
        sender.innerHTML = "Please enter your phone number.";
        args.IsValid = document.getElementById("<%=txtPhoneNumber.ClientID %>").value != ""
    } else if (document.getElementById("<%=rbCellNumber.ClientID %>").checked) {
        sender.innerHTML = "Please enter your cell number.";
        args.IsValid = document.getElementById("<%=txtCellNumber.ClientID %>").value != ""
    } else {
        args.IsValid = false;
    }
}
</script>

26 Feb 2014

Table Layouts vs. Div Layouts: From Hell to… Hell?

Over the last several years, developers have moved from table-based website structures to div-based structures. Hey, that’s great. But wait! Do developers know the reasons for moving to div-based structures, and do they know how to? Often it seems that people are moving away from table hell only to wind up in div hell.
This article covers common problems with layout structure in web design. The first part goes through what table and div hells are, including lots of examples. The next section shows how to write cleaner and more readable code. The final part looks at what features await in future. Please join us on this journey from hell to heaven.

Table Hell

You’re in table hell when your website uses tables for design purposes. Tables generally increase the complexity of documents and make them more difficult to maintain. Also, they reduce a website’s flexibility in accommodating different media and design elements, and they limit a website’s functionality.
MAMA (Metadata Analysis and Mining Application) is a structural Web page search engine from Opera Software that crawls Web pages and returns results detailing page structures. If we look into MAMA’s key findings, we see that the average website has a table structure nested three levels deep. On the list of 10 most popular tags, tabletd and tr are all there. The table element is found on over 80% of the pages whose URLs were crawled by MAMA.
Semantically speaking, the table tag is meant for listing tabular data. It is not optimized to build structure.

EASE OF USE

Using tables to build structure is quite intuitive. We see tabular data every day, and the concept is well known.
And the existence of table attributes makes for a rather flat learning curve because the developer doesn’t have to use a separate style sheet. With divs, the developer must use the style attribute or an external style sheet, because the div tag doesn’t have any attributes attached to it.
Also, tables don’t break when the content is too wide. Columns are not squeezed under other columns as they are in a div-based structure. This adds to the feeling that using tables is safe.

MAINTAINABILITY

Table contains different tags: the table tag being the wrapper, tr for each row and td for each cell. The thead and tbody tags are not used for structural purposes because they add semantic meaning to the content. For readability, each tag is normally given its own line of code, with indention. With all of these tags for the table, several extra lines of code are added to content. The colspan and rowspan attributes make the code even more complex, and any developer maintaining that page in future has to go through a lot of code to understand its structure.
<table cellpadding="0" cellspacing="0" border="0">
 <tr>
  <td colspan="3" height="120px">....</td>
 </tr>
 <tr>
  <td class="menu" valign="top">...</td>
  <td class="content" valign="top">...</td>
  <td class="aSide" valign="top">...</td>
 </tr>
 <tr>
  <td colspan="3">...</td>
 </tr>
</table>
<div id="header">...</div>
<div id="menu">...</div>
<div id="content">...</div>
<div id="aSide">...</div>
<div id="footer">...</div>
As we see from the example, the table-based layout contains more code than the div-based version. Imagine now if this difference in size stays consistent as the code base grows (by a ratio as much as 2:1). In a div-based structure, it is also possible to skip the menu div and use an unordered list (ul) as a container instead.
Nested tables are code smell that a website is stuck in table hell. The number of lines of code is endless, and the complexity is overwhelming. Tables are far from clean code and don’t bring anything semantic to the content unless you’re dealing with actual tabular data. And if you’ve happened to inherit the maintenance of a website that has poor readability, it’s a nightmare. Nested tables are a poor substitution for semantically meaningful, block-level elements.
Another drawback to tables is that they make it harder to separate content from design. The borderwidthcellpadding and cellspacing tags are used in about 90% of all websites that use tables, according to MAMA. This adds code to the HTML that should instead go in the style sheet.
Excess code slows down development and raises maintenance costs. There’s a limit to how many lines of code a programmer can produce per hour, and excess code is more complicated for others to understand. Developers may not even understand their own code after a while.
More lines of code mean larger file sizes, which mean longer download times. Developers should keep in mind new media, such as mobile devices, which usually have low bandwidth. Websites will have to support media other than traditional monitors in future, and bad code limits the possibilities. A large code base has more bugs than a small one. Developers tend to produce a certain number of bugs per line of code. Because tables increase the code base, such structures likely contain more bugs than layouts with less code lines.