Friday, September 16, 2011

get html control content before render


source : http://msdn.microsoft.com/en-us/library/system.web.ui.control.rendercontrol.aspx

Control.RenderControl Method

Outputs server control content and stores tracing information about the control if tracing is enabled.
This member is overloaded. For complete information about this member, including syntax, usage, and examples, click a name in the overload list.
 NameDescription
Public methodRenderControl(HtmlTextWriter)Outputs server control content to a provided HtmlTextWriter object and stores tracing information about the control if tracing is enabled.
Protected methodRenderControl(HtmlTextWriter, ControlAdapter)Outputs server control content to a provided HtmlTextWriter object using a provided ControlAdapter object.
Top

Disable Caching in an HttpHandler

http://weblogs.asp.net/ashicmahtab/archive/2008/09/18/disable-caching-in-an-httphandler.aspx


source : 
I was generating some custom reports in Word 2007 format today. The reports were being served by an HttpHandler and various params are passed to it (mostly by query string). One report needed a list of ids to be passed and the query string wasn't an option there, so I put that in Session. [My other post today shows how]. The trouble was that the urls were identical and someone clever (the browser or the server) was caching the report. So, changing the parameter that was made up of ids resulted in no change of the report. Now, output caching is pretty simple to eliminate on pages, and for asmx web services for that matter, but I found that doing so for a handler is slightly tricky. Here's what I did:
context.Response.Clear();context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetExpires(DateTime.MinValue);
 in the ProcessRequest method.
I would've thought context.Response.Cache.SetCacheability(HttpCacheability.None) would do it, but that kept giving me errors when downloading the docx file. Rather, enabling cacheing and forcing a timeout seems to do it.

Friday, September 9, 2011

Asp.net Ajax UpdatePanel Simultaneous Update - A Remedy


If you ever try to do more than one simultaneous partial update with Asp.net Ajax Update Panel, I guess  you already found  that Asp.net Ajax Framework cancel the current update request and starts the new one. You think I am kidding? Okay lets try the following code:
<%@ Page Language="C#"%>
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            lblDate1.Text = lblDate2.Text = DateTime.Now.ToString();
        }
    }

    protected void btnDate1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate1.Text = DateTime.Now.ToString();
    }

    protected void btnDate2_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate2.Text = DateTime.Now.ToString();
    }
script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Update Paneltitle>
head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        asp:ScriptManager>
        <asp:UpdatePanel ID="updDate1" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                Update Panel 1: <asp:Label ID="lblDate1" runat="server">asp:Label>
                <asp:Button ID="btnDate1" runat="server" Text="Update Time" OnClick="btnDate1_Click" />
            ContentTemplate>
        asp:UpdatePanel>
        <div>
            <asp:UpdatePanel ID="updDate2" runat="server" UpdateMode="Conditional" RenderMode="Inline">
                <ContentTemplate>
                    Update Panel 2: <asp:Label ID="lblDate2" runat="server">asp:Label>
                    <asp:Button ID="btnDate2" runat="server" Text="Update Time" OnClick="btnDate2_Click" />
                ContentTemplate>
            asp:UpdatePanel>
        div>
    form>
body>
html>
The example is quite simple, I have two update panel which UpdateMode is set to Conditional, upon clicking the buttons inside the update panel I am doing a fake delay of 5 seconds then printing the current time of the server in the labels. Okay now run the code and click the first button and then click the second button in 5 seconds so that you can do simultaneous update. What happens, the first label never gets updated? Yes this is the nature of update panel, it cancels the current call and start executing the new one. You can get a more clear picture if you run it in FireFox with FireBug, Open FireFox Click Tools->FireBug->Open FireBug, then move to the Console tab in the firebug and repeat the button clicks, you will find only the last request spinning  animation gets completed.
I can understand the Update Panel does a monsters job for us, executing the full life cycle of the page in the server, generating the updated part of the page, downloading new scripts, updating viewstates etc and that is why it needs to work serially. But how come it discards it current update request once it found a new request? is not it better if it queues the new request and execute it once the current request completes?

Now Consider the following page:
<%@ Page Language="C#"%>
DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            lblDate1.Text = lblDate2.Text = DateTime.Now.ToString();
        }
    }

    protected void btnDate1_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate1.Text = DateTime.Now.ToString();
    }

    protected void btnDate2_Click(object sender, EventArgs e)
    {
        System.Threading.Thread.Sleep(1000 * 5); // Sleeps 5 second
        lblDate2.Text = DateTime.Now.ToString();
    }
script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Update Panel Enhancedtitle>
head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Path="~/Test/PageRequestManagerEx.js" />
            Scripts>
        asp:ScriptManager>
        <script type="text/javascript">
            PageRequestManagerEx.init();
        script>
        <asp:UpdatePanel ID="updDate1" runat="server" UpdateMode="Conditional">
            <ContentTemplate>
                Update Panel 1: <asp:Label ID="lblDate1" runat="server">asp:Label>
                <asp:Button ID="btnDate1" runat="server" Text="Update Time" OnClick="btnDate1_Click" />
            ContentTemplate>
        asp:UpdatePanel>
        <div>
            <asp:UpdatePanel ID="updDate2" runat="server" UpdateMode="Conditional" RenderMode="Inline">
                <ContentTemplate>
                    Update Panel 2: <asp:Label ID="lblDate2" runat="server">asp:Label>
                    <asp:Button ID="btnDate2" runat="server" Text="Update Time" OnClick="btnDate2_Click" />
                ContentTemplate>
            asp:UpdatePanel>
        div>
    form>
body>
html>
The only difference you will find from the previous one that I have added a new JavaScript file PageRequestManagerEx.js and called a method PageRequestManagerEx.init(). Now run this page and repeat the button clicks, you will find the complete source in the bottom of this post. If you run it in FireFox with FireBug, you will find that no matter how many clicks you made in those buttons there is only one concurrent call and once the current call completes it executes another call. Yes as you have guessed the new JavaScript file which I just added does the trick. Now let us see what is in the JavaScript file.
var PageRequestManagerEx =
{
    _initialized : false,

    init : function()
    {
        if (!PageRequestManagerEx._initialized)
        {
            var _callQueue = new Array();
            var _executingElement = null;
            var prm = Sys.WebForms.PageRequestManager.getInstance();

            _prm.add_initializeRequest(initializeRequest);
            _prm.add_endRequest(endRequest);

            PageRequestManagerEx._initialized = true;
        }

        function initializeRequest(sender, args)
        {
            if (_prm.get_isInAsyncPostBack())
            {
                //if we are here that means there already a call pending.

                //Get the element which cause the postback
                var postBackElement = args.get_postBackElement();

                //We need to check this otherwise it will abort the request which we made from the
                //end request
                if (_executingElement != postBackElement)
                {
                    //Does not match which means it is another control
                    //which request the update, so cancel it temporary and 
                    //add it in the call queue
                    args.set_cancel(true);
                    Array.enqueue(_callQueue, postBackElement);
                }

                //Reset it as we are done with our matching
                _executingElement = null;
            }
        }

        function endRequest(sender, args)
        {
            //Check if we have a pending call
            if (_callQueue.length > 0)
            {
                //Get the first item from the call queue and setting it
                //as current executing item
                _executingElement = Array.dequeue(_callQueue);

                //Now Post the from which will also fire the initializeRequest
                _prm._doPostBack(_executingElement.id, '');
            }
        }
    }
}

if (typeof(Sys) != 'undefined')
{
    Sys.Application.notifyScriptLoaded();
}
The PageRequestManagerEx is a Static class which hooks the initializeRequest and endRequest events of the original PageRequestManager. Then in the initializeRequest it first checks if there is an ongoing update, if so it cancel the new request and adds the control in a queue which cause the postback. Then in the endRequest it check if there is any pending call in the queue, if so it executes it. This loops gets executed until all the call in the queue get completed.
Lets me know if you found any issue. I will  be also very curious to know why update panel does not behave like this?
Download : Complete Source

Select a row in an asp:GridView without using a Select Command



ASP.Net's GridViews can be quite useful, but beware of binding them to huge datasets as this has an overhead on the ViewState.
Often you'll want to display a number of columns on each line and row space becomes an issue. What's worse is you then have to create a SELECT command button to be able to access that line's data.
<asp:CommandField ShowInsertButton="true" />
Use the following code on the event OnRowDataBound to eliminate the need for the SELECT command field and save yourself some valuable space.
Here is the HTML to create a GridView, I'm displaying a list of people, and the key for each record is the PERSON_ID.
<asp:GridView ID="PeopleGridView" runat="server" 
    AutoGenerateColumns="False" 
    DataKeyNames="PERSON_ID" 
    DataSourceID="PeopleDataObject" 
    Width="200px" 
    OnRowDataBound="PeopleGridView_RowDataBound" 
    AllowPaging="True">
    <Columns>  
        <asp:BoundField 
            DataField="USER_NAME" 
            HeaderText="Name" 
            SortExpression="USER_NAME" >
        asp:BoundField>
    Columns>
asp:GridView>
The key event to note is the OnRowDataBound, use the following code to create SELECT functionality on the row.
protected void PeopleGridView_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            e.Row.Attributes["onmouseover"] = "this.style.cursor='hand';this.style.textDecoration='underline';";
            e.Row.Attributes["onmouseout"] = "this.style.textDecoration='none';";

            e.Row.Attributes["onclick"] = ClientScript.GetPostBackClientHyperlink(this.PeopleGridView, "Select$" + e.Row.RowIndex);
        }
    }
Each row will then behave like a link, and when you select one it can drive the behavior of another control(s) on your page, possibly a DetailsView allowing you to INSERT a complete record to the database.