Amit's Blog

Sharing Thoughts and Learning

  Home  |   Contact  |   Syndication    |   Login
  43 Posts | 1 Stories | 152 Comments | 14 Trackbacks

News

About Me?
Read it in
Blog Statistics
Proud Member of

Archives

Post Categories

Articles

Book Review

I Visit.

OpenSource Project(s)

Tuesday, September 18, 2007 #

Thanks to Joe Stagner for setting up a blog for me. I will be posting over there from here on. I will also incrementally import the older posts. Please update your links. I am expecting the feed readers will not bothered as the feedburner account will be the same. Sorry for the inconvenience, I promise you will find me for a long time blogging in that space.

Blog Moved
http://weblogs.asp.net/rashid/

Edit: The reason behind I am moving this Blog, I think it is a better place for Asp.net developers where the posts will be evaluated by all the leaders. But my blessing will always be with GWB team for makging such an wonderful place for the blogging..


Monday, September 17, 2007 #

My new article has been just published for September Ajax Column in DotNetSlackers.com. In this article, I have answered few common issues which I found often discussed in Asp.net Ajax Web Service Forum. In this column, I have covered:

  • Web Service Method, succeededCallback and failedCallback methods signature.
  • Complex data type interchange which includes Array, Dictionary, Custom Class.
    • GenerateScriptType Attribute
    • ScriptIgnore Attribute
    • Custom JavaScriptConverter class
  • Progress Indicator for long running task.
  • Soap Header/Custom Http Header

Check it out.

kick it on DotNetKicks.com


Saturday, September 15, 2007 #

In this post, I will show you how to compress the Asp.net Ajax Web Service response, To understand the benefits of compression let us start with a simple example, Consider you have an web service which returns a large data like the following:

[WebMethod()]
public string GetLargeData()
{
    using (StreamReader sr = File.OpenText(Server.MapPath("~/DataFile.txt")))
    {
        return sr.ReadToEnd();
    }
}

The web method reads an large text file (around 100KB) and returns it contents. Once we call this method from a page the network activity in the firebug shows like the following:

Plain

Now, lets examine the HttpModule which compress the Ajax Web Service response. The following shows the complete code of this module:

using System;
using System.IO;
using System.IO.Compression;
using System.Globalization;
using System.Web;


public class JsonCompressionModule : IHttpModule
{
    public JsonCompressionModule()
    {
    }

    public void Dispose()
    {
    }

    public void Init(HttpApplication app)
    {
        app.PreRequestHandlerExecute += new EventHandler(Compress);
    }

    private void Compress(object sender, EventArgs e)
    {
        HttpApplication app = (HttpApplication)sender;
        HttpRequest request = app.Request;
        HttpResponse response = app.Response;

        //Ajax Web Service request is always starts with application/json
        if (request.ContentType.ToLower(CultureInfo.InvariantCulture).StartsWith("application/json"))
        {
            //User may be using an older version of IE which does not support compression, so skip those
            if (!((request.Browser.IsBrowser("IE")) && (request.Browser.MajorVersion <= 6)))
            {
                string acceptEncoding = request.Headers["Accept-Encoding"];

                if (!string.IsNullOrEmpty(acceptEncoding))
                {
                    acceptEncoding = acceptEncoding.ToLower(CultureInfo.InvariantCulture);

                    if (acceptEncoding.Contains("gzip"))
                    {
                        response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
                        response.AddHeader("Content-encoding", "gzip");
                    }
                    else if (acceptEncoding.Contains("deflate"))
                    {
                        response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
                        response.AddHeader("Content-encoding", "deflate");
                    }
                }
            }
        }
    }
}

Next, register this module in the web.config like the following:

<httpModules>
    <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <add name="JsonCompressionModule" type="JsonCompressionModule"/>
</httpModules>

This time the network activity shows like the following:

Compressed Json

So by adding this little module, we have saved 74KB. Now consider the impact of this in an highly traffic ajax web application :-). You will find the complete source code in the bottom of this post. If you want to learn more optimization tips check out my previous post Implement Yahoo's YSlow in your Asp.net pages and Combine Multiple JavaScript and CSS Files and Remove Overheads.

Download: Full Source

kick it on DotNetKicks.com


Thursday, September 13, 2007 #

In my previous post, I have shown the data binding capabilities of ListView control. In this post, I will show the Data Editing power of ListView. Like the previous DataGrid, DataList and GridView controls the ListView control has the EditItemTemplate and it also comes up with the long awaited InsertItemTemplate, in the past there was a lot of attempt to allow the users to insert data through Header or Footer Template which is no longer required. In this post I will show you the full data editing capabilities of ListView which includes Insert, Edit and Delete with both LinqDataSource and code. We will follow the previous post example for the data editing.

The first thing you have to do to add the data editing capabilities is defining the Edit item and Insert item templates. Instead of row kind of style editing like the DataGrid and GridView we will follow the form style editing, this is the another powerful features of ListView that you have the full control on how the html is generated. The following shows the screen-shots of both EditItemTemplate and InsertItemTemplate view:

EditItemTemplate

InsertItemTemplate

The following shows the EditItem and InsertItem Template:

<EditItemTemplate>
    <tr>
        <td colspan="9">
            <table>
                <tbody>
                    <tr>
                        <th>
                            Company:
                        </th>
                        <td>
                            <asp:TextBox ID="CompanyNameTextBox" runat="server" Text='<% #Bind("CompanyName")%>' ValidationGroup="edit"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="CompanyValidator" runat="server" ControlToValidate="CompanyNameTextBox" ErrorMessage="<div>Company cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Contact:
                        </th>
                        <td>
                            <asp:TextBox ID="ContactNameTextBox" runat="server" Text='<% #Bind("ContactName")%>' ValidationGroup="edit"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="ContactValidator" runat="server" ControlToValidate="ContactNameTextBox" ErrorMessage="<div>Contact cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Title:
                        </th>
                        <td>
                            <asp:TextBox ID="ContactTitleTextBox" runat="server" Text='<% #Bind("ContactTitle")%>'></asp:TextBox>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Address:
                        </th>
                        <td>
                            <asp:TextBox ID="AddressTextBox" runat="server" TextMode="MultiLine" Columns="20" Rows="4" Text='<% #Bind("Address")%>' ValidationGroup="edit"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="AddressValidator" runat="server" ControlToValidate="AddressTextBox" ErrorMessage="<div>Address cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            City:
                        </th>
                        <td>
                            <asp:TextBox ID="CityTextBox" runat="server" Text='<% #Bind("City")%>' ValidationGroup="edit"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="CityValidator" runat="server" ControlToValidate="CityTextBox" ErrorMessage="<div>City cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Postal Code:
                        </th>
                        <td>
                            <asp:TextBox ID="PostalCodeTextBox" runat="server" Text='<% #Bind("PostalCode")%>' ValidationGroup="edit"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="PostalCodeValidator" runat="server" ControlToValidate="PostalCodeTextBox" ErrorMessage="<div>Postal code cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Country:
                        </th>
                        <td>
                            <asp:TextBox ID="CountryTextBox" runat="server" Text='<% #Bind("Country")%>' ValidationGroup="edit"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="CountryValidator" runat="server" ControlToValidate="CountryTextBox" ErrorMessage="<div>Country cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Phone:
                        </th>
                        <td>
                            <asp:TextBox ID="PhoneTextBox" runat="server" Text='<% #Bind("Phone")%>' ValidationGroup="edit"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="PhoneValidator" runat="server" ControlToValidate="PhoneTextBox" ErrorMessage="<div>Phone cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="edit"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th></th>
                        <td>
                            <asp:LinkButton ID="UpdateButton" CommandName="Save" runat="server" Text="Update" ValidationGroup="edit"></asp:LinkButton>
                            <asp:LinkButton ID="CancelButton" CommandName="Cancel" runat="server" Text="Cancel" CausesValidation="false"></asp:LinkButton>
                        </td>
                    </tr>
                </tbody>
            </table>
        </td>
    </tr>
</EditItemTemplate>
<InsertItemTemplate>
    <tr>
        <td colspan="9">
            <table>
                <tbody>
                    <tr>
                        <th>
                            ID:
                        </th>
                        <td>
                            <asp:TextBox ID="CustomerIDTextBox" runat="server" Text='<% #Bind("CustomerID")%>' ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="IDValidator" runat="server" ControlToValidate="CustomerIDTextBox" ErrorMessage="<div>ID cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Company:
                        </th>
                        <td>
                            <asp:TextBox ID="CompanyNameTextBox" runat="server" Text='<% #Bind("CompanyName")%>' ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="CompanyValidator" runat="server" ControlToValidate="CompanyNameTextBox" ErrorMessage="<div>Company cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Contact:
                        </th>
                        <td>
                            <asp:TextBox ID="ContactNameTextBox" runat="server" Text='<% #Bind("ContactName")%>' ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="ContactValidator" runat="server" ControlToValidate="ContactNameTextBox" ErrorMessage="<div>Contact cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Title:
                        </th>
                        <td>
                            <asp:TextBox ID="ContactTitleTextBox" runat="server" Text='<% #Bind("ContactTitle")%>'></asp:TextBox>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Address:
                        </th>
                        <td>
                            <asp:TextBox ID="AddressTextBox" runat="server" Text='<% #Bind("Address")%>' TextMode="MultiLine" Columns="20" Rows="4" ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="AddressValidator" runat="server" ControlToValidate="AddressTextBox" ErrorMessage="<div>Address cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            City:
                        </th>
                        <td>
                            <asp:TextBox ID="CityTextBox" runat="server" Text='<% #Bind("City")%>' ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="CityValidator" runat="server" ControlToValidate="CityTextBox" ErrorMessage="<div>City cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Postal Code:
                        </th>
                        <td>
                            <asp:TextBox ID="PostalCodeTextBox" runat="server" Text='<% #Bind("PostalCode")%>' ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="PostalCodeValidator" runat="server" ControlToValidate="PostalCodeTextBox" ErrorMessage="<div>Postal code cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Country:
                        </th>
                        <td>
                            <asp:TextBox ID="CountryTextBox" runat="server" Text='<% #Bind("Country")%>' ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="CountryValidator" runat="server" ControlToValidate="CountryTextBox" ErrorMessage="<div>Country cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th>
                            Phone:
                        </th>
                        <td>
                            <asp:TextBox ID="PhoneTextBox" runat="server" Text='<% #Bind("Phone")%>' ValidationGroup="add"></asp:TextBox>
                            <asp:RequiredFieldValidator ID="PhoneValidator" runat="server" ControlToValidate="PhoneTextBox" ErrorMessage="<div>Phone cannot be blank.</div>" SetFocusOnError="true" Display="Dynamic" ValidationGroup="add"></asp:RequiredFieldValidator>
                        </td>
                    </tr>
                    <tr>
                        <th></th>
                        <td>
                            <asp:LinkButton ID="InsertButton" CommandName="Insert" runat="server" Text="Save" ValidationGroup="add"></asp:LinkButton>
                            <asp:LinkButton ID="CancelButton" CommandName="Cancel" runat="server" Text="Cancel" CausesValidation="false"></asp:LinkButton>
                        </td>
                    </tr>
                </tbody>
            </table>
        </td>
    </tr>
</InsertItemTemplate>

To edit existing record, you have to put a Button in the ItemTemplate/AlternatingItemTemplate which commandName is set to Edit, you can also use ImageButton/LinkButton instead of regular button as in this example I am using LinkButton. The same holds true for deleting record, in this case the commandName has to be Delete. Next, in the EditItemTemplate you have to add buttons which commandName sets to Update and Cancel.

To add record, you must have to set the InsertItemTemplate position, the supported values are FirstItem, LastItem and None. Since we are using form view for adding record it does not look appealing showing it all the time, instead we will put a button in the LayoutTemplate, upon click the form view will appear at the end. Next, in the InsertItemTemplate two new buttons are required which commandName sets to Insert and Cancel. At last, as we are showing this template on the new button click, we need to put few lines of code to show/hide the template as well as the new button. The following shows the code:

private void CloseInsert()
{
    lvwCustomers.InsertItemPosition = InsertItemPosition.None;
    ((LinkButton) lvwCustomers.FindControl("NewButton")).Visible = true;
}

protected void NewButton_Click(object sender, EventArgs e)
{
    lvwCustomers.EditIndex = -1;
    lvwCustomers.InsertItemPosition = InsertItemPosition.LastItem;
    ((LinkButton) sender).Visible = false;
}

protected void lvwCustomers_ItemEditing(object sender, ListViewEditEventArgs e)
{
    CloseInsert();
}

protected void lvwCustomers_ItemCanceling(object sender, ListViewCancelEventArgs e)
{
    if (e.CancelMode == ListViewCancelMode.CancelingInsert)
    {
        CloseInsert();
    }
}

protected void lvwCustomers_ItemInserted(object sender, ListViewInsertedEventArgs e)
{
    CloseInsert();
}

That's it we are all set to Add/Edit/Delete the records with LinqDataSource control.

Now we will see how to do the same with code. It is almost same as the LinqDataSource except few things, we need to hook the ItemCommand event and add few empty event handlers, the following shows the code:

protected void lvwCustomers_ItemCommand(object sender, ListViewCommandEventArgs e)
{
    switch(e.CommandName)
    {
        case "Insert":
        {
            InsertCustomer(e.Item);
            break;
        }
        case "Update":
        {
            UpdateCustomer(e.CommandArgument as string, e.Item);
            break;
        }
        case "Delete":
        {
            DeleteCustomer(e.CommandArgument as string);
            break;
        }
    }
}

protected void lvwCustomers_ItemInserting(object sender, ListViewInsertEventArgs e)
{
}

protected void lvwCustomers_ItemEditing(object sender, ListViewEditEventArgs e)
{
    CloseInsert();
    lvwCustomers.EditIndex = e.NewEditIndex;
    BindList();
}

protected void lvwCustomers_ItemUpdating(object sender, ListViewUpdateEventArgs e)
{
}

protected void lvwCustomers_ItemCanceling(object sender, ListViewCancelEventArgs e)
{
    if (e.CancelMode == ListViewCancelMode.CancelingInsert)
    {
        CloseInsert();
    }
    else
    {
        lvwCustomers.EditIndex = -1;
    }

    BindList();
}

protected void lvwCustomers_ItemDeleting(object sender, ListViewDeleteEventArgs e)
{
}

As you can see that in ItemCommand event we are checking the commandName and based upon that we are calling the helper function which does the actual db operation. The reason behind having those empty event handlers like ItemInserting, ItemUpdating and ItemDeleting is, if those left unhandled you will get a nasty exception that the event was not handled. While playing with the ListView data editing my first try was these events to do the db operation instead of the ItemCommand, but found that the event argument always left empty(except the itemIndex) as well as the EditItem and InsertItem property of ListView if the ListView is not bind with any DataSource control.

Now lets see what those db operation functions does, the following shows the code:

private void InsertCustomer(ListViewItem insertItem)
{
    if (IsValid)
    {
        using (NorthwindDataContext db = new NorthwindDataContext())
        {
            Customer customer = new Customer();

            customer.CustomerID = ((TextBox)insertItem.FindControl("CustomerIDTextBox")).Text;
            customer.CompanyName = ((TextBox)insertItem.FindControl("CompanyNameTextBox")).Text;
            customer.ContactName = ((TextBox)insertItem.FindControl("ContactNameTextBox")).Text;
            customer.ContactTitle = ((TextBox)insertItem.FindControl("ContactTitleTextBox")).Text;
            customer.Address = ((TextBox)insertItem.FindControl("AddressTextBox")).Text;
            customer.City = ((TextBox)insertItem.FindControl("CityTextBox")).Text;
            customer.PostalCode = ((TextBox)insertItem.FindControl("PostalCodeTextBox")).Text;
            customer.Country = ((TextBox)insertItem.FindControl("CountryTextBox")).Text;
            customer.Phone = ((TextBox)insertItem.FindControl("PhoneTextBox")).Text;

            db.Customers.Add(customer);

            db.SubmitChanges();
            CloseInsert();
            BindList();
        }
    }
}

private void UpdateCustomer(string customerID, ListViewItem editItem)
{
    if (IsValid)
    {
        using (NorthwindDataContext db = new NorthwindDataContext())
        {
            Customer customer = db.Customers.Where(c => c.CustomerID == customerID).Single();

            customer.CompanyName = ((TextBox) editItem.FindControl("CompanyNameTextBox")).Text;
            customer.ContactName = ((TextBox)editItem.FindControl("ContactNameTextBox")).Text;
            customer.ContactTitle = ((TextBox)editItem.FindControl("ContactTitleTextBox")).Text;
            customer.Address = ((TextBox)editItem.FindControl("AddressTextBox")).Text;
            customer.City = ((TextBox)editItem.FindControl("CityTextBox")).Text;
            customer.PostalCode = ((TextBox)editItem.FindControl("PostalCodeTextBox")).Text;
            customer.Country = ((TextBox)editItem.FindControl("CountryTextBox")).Text;
            customer.Phone = ((TextBox)editItem.FindControl("PhoneTextBox")).Text;

            db.SubmitChanges();
            lvwCustomers.EditIndex = -1;
            BindList();
        }
    }
}

private void DeleteCustomer(string customerID)
{
    using(NorthwindDataContext db = new NorthwindDataContext())
    {
        Customer customer = db.Customers.Where(c => c.CustomerID == customerID).Single();
        db.Customers.Remove(customer);
        db.SubmitChanges();
    }

    Pager dpCustomers = (Pager)lvwCustomers.FindControl("dpCustomers");
    dpCustomers.CurrentPageNo = 1;

    BindList();
}

That's it. You will find the complete source code of both of this example in the bottom of this post. In my next post, I will show the new GroupTemplate of ListView.

Download: Full Source

kick it on DotNetKicks.com


Sunday, September 09, 2007 #

In my previous post, I have presented few cool css examples of the new Asp.net ListView control. In this post, I will show how you can setup the data binding of the ListView control with the new LinqDataSource control as well as through code, which outputs the previous post examples.

Like its ancestor, the ListView comes up with few templates and some of them are really new. In order to bind data we first have to define the LayoutTemplate. The LayoutTemplate is a new template that acts as a container for either group or item templates. Since our previous example presents the data in tabular format, we will exclude the GroupTemplate from discussing in this post. One of the required thing you have to keep in mind while defining the LayoutTemplate is, you must have to declare an itemContainer/groupContainer where the item/group will be repeated. For example, I have declared the tbody as the itemContainer like the following:

<LayoutTemplate>
    <table>
        <thead>
            <tr>
                <th>Company</th>
                <th>Contact</th>
                <th>Title</th>
                <th>Address</th>
                <th>City</th>
                <th>Postal Code</th>
                <th>Country</th>
                <th>Phone</th>
            </tr>
        </thead>
        <tbody id="itemContainer" runat="server"></tbody>
        <tfoot>
            <tr>
                <th style="text-align:right" colspan="8">
                    <asp:DataPager runat="server" ID="dpCustomers" PageSize="10">
                        <Fields>
                            <asp:NumericPagerField ButtonCount="5"/>
                        </Fields>
                    </asp:DataPager>
                </th>
            </tr>
        </tfoot>
    </table>
</LayoutTemplate>

As you can see, the LayoutTemplate also contains another new control DataPager that I will discuss later. Another important thing is that unlike the Repeater control where usually the HeaderTemplate contains the starting tag and the FooterTemplate contains the ending tag of the container , the LayoutTemplate contains the both except the repeating part.

Next, we have to define the ItemTemplate and the AlteringItemTemplate like the following:

<ItemTemplate>
    <tr>
        <td>
            <asp:Label ID="lblCompany" runat="server" Text='<% #Eval("CompanyName")%>'></asp:Label>
        </td>
        <td>
            <asp:Label ID="lblContact" runat="server" Text='<% #Eval("ContactName")%>'></asp:Label>
        </td>
        <td>
            <asp:Label ID="lblTitle" runat="server" Text='<% #Eval("ContactTitle")%>'></asp:Label>
        </td>
        <td>
            <asp:Label ID="lblAddress" runat="server" Text='<% #Eval("Address")%>'></asp:Label>
        </td>
        <td>
            <asp:Label ID="lblCity" runat="server" Text='<% #Eval("City")%>'></asp:Label>
        </td>
        <td>
            <asp:Label ID="lblPostalCode" runat="server" Text='<% #Eval("PostalCode")%>'></asp:Label>
        </td>
        <td>
            <asp:Label ID="lblCountry" runat="server" Text='<% #Eval("Country")%>'></asp:Label>
        </td>
        <td>
            <asp:Label ID