Twitter Feed Popout byInfofru

Maintain Scroll Position With Asp.net Validation Controls.

In this post I will demonstrate you how to maintain the scroll position of asp.net validation controls on a page

Few days ago, when I was trying to put validation controls on a form which is at the bottom of the page. I noticed that each time Asp.net validation controls executed it reset the scroll position to top and the end user see no messages until page scroll back to the old position. Irritated :)

Problem:

Untitled

 

So, In this post I will demonstrate you how to maintain the scroll position of asp.net validation controls on a page. To start with an example, lets create a form like below

<div>
     <p>

 </p>
 <p>
     &nbsp;</p>
 <p>
     &nbsp;</p>

         <p>
             &nbsp;</p>

         <p>
             &nbsp;</p>
         <p>
             &nbsp;</p>
         <p>
             &nbsp;</p>
         <p>
             &nbsp;</p>
         <p>
             &nbsp;</p>
 <p>
     &nbsp;</p>
 <p>
     &nbsp;</p>
 <p>
     &nbsp;</p>
     <table class="style1">
         <tr>
             <td>
                 Name</td>
             <td>
                 <asp:TextBox ID="txtName" runat="server" autocomplete="off" Width="200"></asp:TextBox>
                 <asp:RegularExpressionValidator ID="revName" runat="server" 
                     ControlToValidate="txtName" Display="None" 
                     ErrorMessage="Please enter a valid name" 
                     ValidationExpression="(^([0-9-a-z-A-Z\\s+?\\.+?\\(+?\\)+?])+$)" 
                     ValidationGroup="grpTop"></asp:RegularExpressionValidator>
                     <asp:RequiredFieldValidator ID="rfvName" runat="server" 
                     ControlToValidate="txtName" Display="None" ErrorMessage="Please enter a name." 
                     SetFocusOnError="true" ValidationGroup="grpTop"></asp:RequiredFieldValidator>
             </td>
         </tr>
         <tr>
             <td>
                 Email Address</td>
             <td>
                 <asp:TextBox ID="txtUsername" runat="server" autocomplete="off" Text="" 
                     Width="200"></asp:TextBox>
                 <asp:RequiredFieldValidator ID="rfvEmailAddress" runat="server" 
                     ControlToValidate="txtUsername" Display="None" 
                     ErrorMessage="Please enter a valid email address." ValidationGroup="grpTop"></asp:RequiredFieldValidator>
                 <asp:RegularExpressionValidator ID="revEmail" runat="server" 
                     ControlToValidate="txtUsername" Display="None" 
                     ErrorMessage="Please key in a valid email address." SetFocusOnError="true" 
                     ValidationExpression="^([a-zA-Z0-9_'+*$%\\^&amp;!\\.\\-])+\\@(([a-zA-Z0-9\\-])+\\.)+([a-zA-Z0-9:]{2,4})+$" 
                     ValidationGroup="grpTop"></asp:RegularExpressionValidator>
                 <asp:CustomValidator ID="valEmailAlreadyExists" runat="server" Display="None" 
                     ErrorMessage="Email address already exists" ValidationGroup="grpTop"></asp:CustomValidator>
             </td>
         </tr>
         <tr>
             <td>
                 Date Format</td>
             <td>
                 <asp:DropDownList ID="ddlDateFormat" runat="server" Width="200px">
                     <asp:ListItem>Select</asp:ListItem>
                     <asp:ListItem Text="dd/MM/yyyy" Value="dd/MM/yyyy">dd/MM/yyyy</asp:ListItem>
                     <asp:ListItem Text="MM/dd/yyyy" Value="MM/dd/yyyy">MM/dd/yyyy</asp:ListItem>
                     <asp:ListItem Text="dd/MM/yy" Value="dd/MM/yy">dd/MM/yy</asp:ListItem>
                     <asp:ListItem Text="MM/dd/yy" Value="MM/dd/yy">MM/dd/yy</asp:ListItem>
                 </asp:DropDownList>
                 <asp:RequiredFieldValidator ID="rfvDateFormat" runat="server" 
                     ControlToValidate="ddlDateFormat" Display="None" 
                     ErrorMessage="Please select date format." InitialValue="Select" 
                     SetFocusOnError="true" ValidationGroup="grpTop"></asp:RequiredFieldValidator>
             </td>
         </tr>
         <tr>
             <td>
                 &nbsp;</td>
             <td>
                 <asp:CheckBox ID="cbIsAdmin" runat="server" Checked="false" 
                     Text="Administrator?" />
                 &nbsp;<asp:CheckBox ID="cbResetPW" runat="server" Checked="false" 
                     Text="Reset Password" />
             </td>
         </tr>
         <tr>
             <td>
                 &nbsp;</td>
             <td>
                 <asp:Button ID="Button1" runat="server" Text="Submit Group 1"  ValidationGroup="grpTop"
                     onclick="Button1_Click" />
                 <asp:ValidationSummary ID="ValidationSummary1" runat="server" 
                     EnableClientScript="true" HeaderText="Fix the following issues :" 
                     ValidationGroup="grpTop" />
             </td>
         </tr>
         <tr>
             <td>
                 &nbsp;</td>
             <td>
                 &nbsp;</td>
         </tr>
     </table>
</div>

Notice that <p> tag which I have given in the above snippet to keep my form at the bottom of the page so that the scroll position problem can easily be demonstrated.

When you run the above snippet, you will realize that the page scroll position will lost as soon as you want to submit the form. If you try the page directive System.Web.Ui.Page.MaintainScrollPositionOnPostBack. Noting will change because by using this property Asp.net engine will take care of scroll position after post back. But we want it on client script. It means, we need to modify the mechanism on which asp.net validation controls executed.

To start with the understanding, I came across a very good article “Understanding ASP.NET Validation Library” by DeepakRaghavan. Which gives me the understanding of two methods. Page_ClientValidate and WebForm_OnSubmit and ultimately I need to modify them and bear in mind that these function will be written inside form tag not in title.

The idea is to use the bookmark to maintain the scroll bar. So lets modify both the functions respectively.

 

Page_ClientValidate:

   1: var bookMark = "#OtherBookMark";
   2: function Page_ClientValidate(validationGroup)
   3: {
   4:   
   5:   Page_InvalidControlToBeFocused = null;  
   6:   if (typeof(Page_Validators) == "undefined") {  
   7:   return true;  
   8:   }  
   9:   var i;  
  10:   for (i = 0; i < Page_Validators.length; i++) {  
  11:   ValidatorValidate(Page_Validators[i], validationGroup, null);  
  12:   }  
  13:   ValidatorUpdateIsValid();  
  14:   ValidationSummaryOnSubmit(validationGroup);  
  15:   Page_BlockSubmit = !Page_IsValid;  
  16:   
  17:   if (validationGroup == "grpTop")
  18:   {
  19:       bookMark = "#addForm";
  20:   }
  21:   else
  22:   {
  23:       bookMark = "#OtherBookMark";
  24:   }
  25:   return Page_IsValid;  
  26: }

Description:

Line No 1.       A variable that will hold the bookmark name

Line No 2-14   The default function implementation (We have simple nothing to do with it… simply copy / paste)

Line No 15- 22 Since we can have multiple forms on a page, that is why it is better to keep track that which validation group is called and decide the bookmark name accordingly.

 

Now instead of modifying WebForm_OnSubmit, let’s first create a following function

SubmitAction:

 

   1: function SubmitAction() {
   2: if (typeof(ValidatorOnSubmit) == "function" && ValidatorOnSubmit() == false)
   3:   {
   4:           var hashIndex = location.href.indexOf("#");
   5:           
   6:           if (hashIndex > -1) 
   7:           {
   8:               var loc = location.href.substring(0,hashIndex);
   9:               location.href = loc + bookMark;
  10:           }
  11:           else
  12:           {
  13:               location.href = location + bookMark;
  14:           }
  15:           
  16:           return false;
  17:   }
  18:   else
  19:   {
  20:       return true;
  21:   }
  22: }

 

Description:

Well, the function is very simple and don’t really need line by line commentary.
We are checking if the validation is fail, set the bookmark. other wise just let it go.

 

WebForm_OnSubmit:

Now we will call the above SubmitAction function inside WebForm_OnSubmit. To inject this function you need to write the following lines on the Page_Load

   1: if (!Page.ClientScript.IsOnSubmitStatementRegistered("keySubmitAction"))
   2: {
   3:     Page.ClientScript.RegisterOnSubmitStatement(this.GetType(), "keySubmitAction", "return SubmitAction();");
   4: }

System.Web.UI.Page.ClientScript.RegisterOnSubmitStatement is actually used to inject your script inside WebForm_OnSubmit. To be more specific, the first lines will be yours :).

and if you notice the Line No : 3 I used return statement which means as soon as WebForm_OnSubmit will execute my function (SubmitAction) will run first and then return the execution out… no more processing on WebForm_OnSubmit.

Now when you look at the view source of your browser you will see some thing similar to the following.

sc_mvc

Ok, now we only need to setup our bookmark.

Just give the id “addForm” to the table or div inside which the validation summary control is placed. that’s it.  However, you can download the VS 2008 Solution from here.

How to show and select month/year in Calendar Extender

In this post I will explain you, How to make calendar extender control to show month / year view by default and instead of selecting dates how can we use calendar extender to select months.

Before I start, let me say that I got extensive support from this forum post http://forums.asp.net/t/1349086.aspx. Thanks to Zhi-Qiang Ni, but the way he follow was a little bit lengthy. However, all credit still goes to him because I gain the exact idea from his post.

Let me start by creating a calendar extender control and attach it to a textbox.

<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<cc1:CalendarExtender ID="TextBox1_CalendarExtender" runat="server" OnClientHidden="onCalendarHidden"  OnClientShown="onCalendarShown" BehaviorID="calendar1"
    Enabled="True" TargetControlID="TextBox1">
</cc1:CalendarExtender>


Now, in extender markup, notice onClientHidden and OnClientShown event which I implemented as below.

function onCalendarShown() {
 
     var cal = $find("calendar1");
     //Setting the default mode to month
     cal._switchMode("months", true);
     
     //Iterate every month Item and attach click event to it
     if (cal._monthsBody) {
         for (var i = 0; i < cal._monthsBody.rows.length; i++) {
             var row = cal._monthsBody.rows[i];
             for (var j = 0; j < row.cells.length; j++) {
                 Sys.UI.DomEvent.addHandler(row.cells[j].firstChild, "click", call);
             }
         }
     }
 }
 
 function onCalendarHidden() 
 {
     var cal = $find("calendar1");
     //Iterate every month Item and remove click event from it
       if (cal._monthsBody) {
         for (var i = 0; i < cal._monthsBody.rows.length; i++) {
             var row = cal._monthsBody.rows[i];
             for (var j = 0; j < row.cells.length; j++) {
                 Sys.UI.DomEvent.removeHandler(row.cells[j].firstChild,"click",call);
             }
         }
     }
 
 }

Pretty simple, In onCalendarShown method I just set the default mode to month and then iterate the control to get month item and attach on click event to it. So that, it will not go further to show us dates of that month and select the first day of that month instead.

Where as, In onCalendarHidden I am simply detaching the click event from month items. Now notice the last parameter of Sys.UI.DomEvent.addHandler function, it is the name of the function which will do the rest of the magic as below.

function call(eventElement)
        {
            var target = eventElement.target;
            switch (target.mode) {
            case "month":
                var cal = $find("calendar1");
                cal._visibleDate = target.date;
                cal.set_selectedDate(target.date);
                cal._switchMonth(target.date);
                cal._blur.post(true);
                cal.raiseDateSelectionChanged();
                break;
            }
        }

Here we are simply selecting the month as the selected date of calendar control. and finally the control will look like as below.

cal_extender

You can get the source code from here :
http://cid-cdbfe38dc780f729.skydrive.live.com/self.aspx/.Public/Calendar%20Extender%20Month.zip

PMP Preparation Blog

One of my friend Sarim Shehkhani has created a blog on PMP Preperation. I red that blog and found it very useful for anyone who is planning to get PMP. Here is the URL : http://pmpforidiots.blogspot.com/Sarim Shehkhani is him self a PMP and got 7+ years experience in Project Management and he is the one who introduces Visual Studio Team Foundation System to me. So, I am looking forward to get some more professional project management skils from PMP for Idiots.

Show images on Grid View from File Stream SQL Server 2008

Background :

In my last post about SQL Server 2008 new feature File Stream (Saving and Retrieving File Using FileStream SQL Server 2008), we did an example of saving an image to the file stream and then retrieve it back and make it available for download.The result of that example looks like as below.fs_snapshot1But, one has to press the button to download the image file. One of my blog reader raise a point that he wants to display the same image instead of Get File button which is going to download.

Introduction :

So, in this post I will explain you, how can we rendered the image before actually downloading it and show that in the grid (Maybe as thumbnail, but this post will not discuss any thing about generating thumbnails).Note : If you want to know. How to add files to the file stream please see my post Saving and Retrieving File Using FileStream SQL Server 2008

Getting Started:

We will complete this goal by using HttpHandler. lets first alter our gridview.

   1: <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
   2:         onrowcommand="GridView1_RowCommand">
   3:         <Columns>
   4:             <asp:BoundField DataField="ID" HeaderText="ID" />
   5:             <asp:BoundField DataField="SystemNumber" HeaderText="System Id" />
   6:             <asp:TemplateField>
   7:                 <ItemTemplate>
   8:                     <asp:LinkButton ID="lbGetFile" runat="server" CommandName="GetFile" CommandArgument='<%#Eval("ID") %>' ><img src='<%#Eval("ID") %>.jpg' /></asp:LinkButton>
   9:                 </ItemTemplate>
  10:             </asp:TemplateField>
  11:         </Columns>
  12:
  13:     </asp:GridView>

Notice the link button on line no 8. I have now specify an image tag inside Link button and pass the primarykey of tbl_files as the file name along with random “.jpg”. So that, it can finally looks like as follows8e7af927-cc7e-4515-8409-d94566246de8.jpga3de6abb-382f-484c-822c-7f93e0ede0c7.jpg4ad64bf1-ea6e-4228-bdc0-300a0cd90f5a.jpgThe idea is, I will attach the handler with jpg file type to accommodate the incoming requests.Now, lets create HttpHandler and name it “imageHandler”

public class imageHandler : IHttpHandler
{
    #region IHttpHandler Members
    public bool IsReusable
    {
        get { return false; }
    }
    public void ProcessRequest(HttpContext context)
    {
        //Getting file name from incoming request.
        string url = Path.GetFileName(context.Request.Path);
        Guid FileId;
        try
        {
            //Since we have all our primary keys stored in GUID
            //Try parsing the file name to Guid
            FileId = new Guid(Path.GetFileNameWithoutExtension(url));
        }
        catch (FormatException)
        {
            //If some other JPG file is requested
            FileId = Guid.Empty;
        }
        if (FileId != Guid.Empty) // If the call is for valid Image File Stream
        {
            SqlConnection objSqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
            objSqlCon.Open();
            SqlTransaction objSqlTran = objSqlCon.BeginTransaction();
            SqlCommand objSqlCmd = new SqlCommand("FileGet", objSqlCon, objSqlTran);
            objSqlCmd.CommandType = CommandType.StoredProcedure;
            SqlParameter objSqlParam1 = new SqlParameter("@ID", SqlDbType.VarChar);
            objSqlParam1.Value = FileId.ToString();
            objSqlCmd.Parameters.Add(objSqlParam1);
            string path = string.Empty;
            string fileType = string.Empty;
            using (SqlDataReader sdr = objSqlCmd.ExecuteReader())
            {
                while (sdr.Read())
                {
                    path = sdr[0].ToString();
                    fileType = sdr[1].ToString();
                }
            }
            objSqlCmd = new SqlCommand("SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()", objSqlCon, objSqlTran);
            byte[] objContext = (byte[])objSqlCmd.ExecuteScalar();
            SqlFileStream objSqlFileStream = new SqlFileStream(path, objContext, FileAccess.Read);
            byte[] buffer = new byte[(int)objSqlFileStream.Length];
            objSqlFileStream.Read(buffer, 0, buffer.Length);
            objSqlFileStream.Close();
            objSqlTran.Commit();
            context.Response.AddHeader("Content-disposition", "attachment; filename=" + Path.GetFileName(path) + fileType);
            // Here you need to manage the download file stuff according to your need
            context.Response.ContentType = "application/octet-stream";
            context.Response.BinaryWrite(buffer);
        }
        else
        { // If the call is for some other JPG file, nothing to do with file stream.
            context.Response.WriteFile(context.Request.Path);
        }
    }

Well, read the comments I wrote in the code. That will of course help you to understand what actually I have done.And then register the HttpHandler.

<httpHandlers>
    <add verb="*" path="*.jpg" type="LearningApp.imageHandler, LearningApp"/>
</httpHandlers>

Conclusion:

There we go, In this way we can show the images stored on file stream in grid view. You can download both VS 2008 and VS 2010 project files.

Team Foundation Server Installation Story

In my life, whenever I attempt to install Team Foundation Server (TFS). I experienced some new issues, fixes, updates and work around and we are now again moving to TFS for source controlling and project management. So, this time I planned to put my installation experience on a blog.

In my knowledge TFS is the only Microsoft technology, that doesn’t work with Next Next stuff. You need to be sure what you are doing when you are installing things.

In this post I will explain you the process of installing TFS to the new machine.

Pre-Requirement:

1. SQL Server 2005 With Reporting and Analysis services. (Service Pack 2)
2. Install SharePoint Services 2007.
3. Once installed, go to administration tool and run SharePoint Product and Technologies Configuration wizard. Ignore the popup saying that
"The following services may have to be started or reset during configuration:
Internet Information Services
SharePoint Administration Service
SharePoint Timer Service".
Just press ok and let it go.

Once the the Wizard is finish, A browser will open containing the SharePoint site.
If it open up with the error, Go to IIS and see if there is an other site which is using SharePoint. If yes, then either you can change port of your site or you can also change SharePoint default port. For that see the following post.

http://www.theangrycoder.net/sharepoint/how-to-change-default-port-of-sharepoint.html

4. Now go to the Central Administration Site and see the Administration Task. And complete configure outgoing email settings and create new web applications task which will ensure your that SharePoint is successfully installed and able to create web sites with no problem.

I have no idea, what went wrong but my reporting services doesn’t create any database neither it was configures on my machine. So I waste some time to do it myself.

Configure Reporting Services:

  1. Now go to Programs – SQL Server 2005 - Configuration Tools - Reporting Service Configuration.
  2. Click on the Tab on the left called "Report Server Virtual Directory" and then simply press create button on the form. Give appropriate web site on the popup "Default Website" in my case.
  3. Repeat the Step six for "Report Manager Virtual Directory".
  4. Go to Database Setup Tab and if you cannot see any thing in database drop down. Click on new button. A screen will appear. Give correct credentials to create new database. Once your are done with new DB creation. Add user name and password on the main form and click apply.

I have not put the TFS DVD yet, all this was a pre-installation stuff.

Installation:

  1. Now go to the TFS Installation screen and run Windows SharePoint Service Extension, but make sure you have successfully installed SharePoint before this.
  2. At the end of the setup a popup will appear which will ask for the following URL.
  3. SharePoint Site URL (Which will be your default SharePoint Site)
    Reports
    ReportServer

    Last two are the virtual directories which will be find either under your default website in IIS, or under the website running on port 80. So, you have to check it manually and give the path.
  4. Then Run Team Foundation Server Installation but before that make sure you have done following things.

    Create a user that has full rights on SharePoint Administration. In my case, I simply, create an admin user with different user id and password. You need to provide this user authentication information during installation.

    Set SQL Server Agent / SQL Server browser Services to Auto Start.

Yeah, this was the story of my last 1.5 days :). It is my thinking, that TFS always comes up with some unique problem. So you might face some thing that is not listed here. In that case, Create post on your blog and share with us.

Cheers everyone :)

How to change default port of SharePoint

In this post, I will explain you how can we change the default port of SharePoint. 
To do that, take the following steps.

  1. Goto IIS, Right Click on Sharepoint Site and change the port (Lets say to 90)
  2. Goto Sharepoint Central Administration, Click on the operation tab then Alternate acccess mapping under global settings.
  3. Now you can see the list of sharepoint sites. Now edit the one without port, and alter the URL. in My case I change it from
    http://srv-production to http://srv-production:91

And you are done with port stuff.

Custom Paging in Grid view

So, it takes too long for me to write this post. even though I completed the coding stuff a week back but it is really hard to manage time these days.In this post, I will explain and demonstrate you how to create custom paging in Grid view control. Paging which works like the Google Paging. Let me elaborate more, say for example you have a record set of 500 items and you want to display 10 items per page. Now what happen to the pages numbers. Either you use the default with “..” sign after 10th page link which cause the post back and then get some new page numbers. Or you want to make it like Google. i.e as soon as user move forward on page index, hide the previous pages and show the new numbers and when user is getting back hide new numbers and show the previous page links. following image can give you some idea what we are going to do.gvpaging_scgvpaging_sc2To start, lets first create a Grid view .
   1: <asp:GridView ID="GridView1" runat="server" AllowPaging="True"
   2:     onrowcreated="GridView1_RowCreated" >
   3: </asp:GridView>
Yes, we have allow the paging but we are not going to use the default paging of asp.net. That is why we have write onrowcreated implementation in which we will simply detect and hide the pager row.
   1: protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
   2:  {
   3:      if (e.Row.RowType == DataControlRowType.Pager)
   4:      {
   5:          e.Row.Visible = false;
   6:      }
   7:  }
Now lets move to some global variables which we need through out our code.
   1: const int pageSize = 10;
   2: const int pageDispCount = 10;
   3: private DataTable dt = new DataTable();
pagesSize : number of records we want to display per page.pageDispCount : number of page numbers we want to display on custom paging.dt : A datatable which we will use to store data and use it on different post backs.Ok, now we need to get data from database, dump it to datatable and define the datasource of grid view.
   1: protected void bindData()
   2: {
   3:     SqlConnection objSqlCon = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["AdventureWorksConnectionString"].ToString());
   4:     objSqlCon.Open();
   5:
   6:     SqlDataAdapter objSqlDa = new SqlDataAdapter("select * from Production.Product", objSqlCon);
   7:
   8:     objSqlDa.Fill(dt);
   9:
  10:     GridView1.DataSource = dt;
  11:     GridView1.DataBind();
  12:
  13:     managePaging(dt);
  14: }
On line number 13, managePaging function will be used later to implement the paging logic. But before that, lets understand that we have taken the datatable as a global variable and each time after postback when we try to read our datatable we will get no results because there is no state management for this object.So lets override the LoadViewState and SaveViewState function of System.Web.UI.Page to save and restore the datatable by using viewstate.
   1: protected override object SaveViewState()
   2: {
   3:   object baseState = base.SaveViewState();
   4:   return new object[] { baseState, dt };
   5: }
   6: protected override void LoadViewState(object savedState)
   7: {
   8:   object[] myState = (object[])savedState;
   9:   if (myState[0] != null)
  10:       base.LoadViewState(myState[0]);
  11:
  12:   if (myState[1] != null)
  13:   {
  14:       dt = (DataTable) myState[1];
  15:       GridView1.DataSource = dt;
  16:       GridView1.DataBind();
  17:
  18:       managePaging(dt);
  19:   }
  20:
  21:   }
Well, SaveViewState function is simply putting the base.SaveViewState object and datatable in and object and returning it. A Simple Logic :)Where as, LoadViewState is retrieving and type casting the object exactly in the sequence it was save in the SaveViewState method.
   1: protected void managePaging(DataTable _dt)
   2: {
   3:     if (_dt.Rows.Count > 0)
   4:     {
   5:
   6:         // Variable declaration
   7:         int numberOfPages;
   8:         int numberOfRecords = dt.Rows.Count;
   9:         int currentPage = (GridView1.PageIndex);
  10:         StringBuilder strSummary = new StringBuilder();
  11:
  12:
  13:         // If number of records is more then the page size (specified in global variable)
  14:         // Just to check either gridview have enough records to implement paging
  15:         if (numberOfRecords > pageSize)
  16:         {
  17:             // Calculating the total number of pages
  18:             numberOfPages = (int)Math.Ceiling((double)numberOfRecords / (double)pageSize);
  19:         }
  20:         else
  21:         {
  22:             numberOfPages = 1;
  23:         }
  24:
  25:
  26:         // Creating a small summary for records.
  27:         strSummary.Append("Displaying <b>");
  28:
  29:         // Creating X f X Records
  30:         int floor = (currentPage * pageSize) + 1;
  31:         strSummary.Append(floor.ToString());
  32:         strSummary.Append("</b>-<b>");
  33:         int ceil = ((currentPage * pageSize) + pageSize);
  34:
  35:         //let say you have 26 records and you specified 10 page size, 
  36:         // On the third page it will return 30 instead of 25 as that is based on pageSize
  37:         // So this check will see if the ceil value is increasing the number of records. Consider numberOfRecords
  38:         if (ceil > numberOfRecords)
  39:         {
  40:             strSummary.Append(numberOfRecords.ToString());
  41:         }
  42:         else
  43:         {
  44:             strSummary.Append(ceil.ToString());
  45:         }
  46:
  47:         // Displaying Total number of records Creating X of X of About X records.
  48:         strSummary.Append("</b> of About <b>");
  49:         strSummary.Append(numberOfRecords.ToString());
  50:         strSummary.Append("</b>Records</br>");
  51:
  52:
  53:         litPagingSummary.Text =  strSummary.ToString();
  54:
  55:
  56:         //Variable declaration 
  57:         //these variables will used to calculate page number display
  58:         int pageShowLimitStart = 1;
  59:         int pageShowLimitEnd = 1;
  60:
  61:
  62:
  63:         // Just to check, either there is enough pages to implement page number display logic.
  64:         if (pageDispCount > numberOfPages)
  65:         {
  66:             pageShowLimitEnd = numberOfPages; // Setting the end limit to the number of pages. Means show all page numbers
  67:         }
  68:         else
  69:         {
  70:             if (currentPage > 4) // If page index is more then 4 then need to less the page numbers from start and show more on end.
  71:             {
  72:                 //Calculating end limit to show more page numbers
  73:                 pageShowLimitEnd = currentPage + (int)(Math.Floor((decimal)pageDispCount / 2));
  74:                 //Calculating Start limit to hide previous page numbers
  75:                 pageShowLimitStart = currentPage - (int)(Math.Floor((decimal)pageDispCount / 2));
  76:             }
  77:             else
  78:             {
  79:                 //Simply Displaying the 10 pages. no need to remove / add page numbers
  80:                 pageShowLimitEnd = pageDispCount;
  81:             }
  82:         }
  83:
  84:         // Since the pageDispCount can be changed and limit calculation can cause < 0 values 
  85:         // Simply, set the limit start value to 1 if it is less
  86:         if (pageShowLimitStart < 1)
  87:             pageShowLimitStart = 1;
  88:
  89:
  90:         //Dynamic creation of link buttons
  91:
  92:         // First Link button to display with paging
  93:         LinkButton objLbFirst = new LinkButton();
  94:         objLbFirst.Click += new EventHandler(objLb_Click);
  95:         objLbFirst.Text = "First";
  96:         objLbFirst.ID = "lb_FirstPage";
  97:         objLbFirst.CommandName = "pgChange";
  98:         objLbFirst.EnableViewState = true;
  99:         objLbFirst.CommandArgument = "1";
 100:
 101:         //Previous Link button to display with paging
 102:         LinkButton objLbPrevious = new LinkButton();
 103:         objLbPrevious.Click += new EventHandler(objLb_Click);
 104:         objLbPrevious.Text = "Previous";
 105:         objLbPrevious.ID = "lb_PreviousPage";
 106:         objLbPrevious.CommandName = "pgChange";
 107:         objLbPrevious.EnableViewState = true;
 108:         objLbPrevious.CommandArgument = currentPage.ToString();
 109:
 110:
 111:         //of course if the page is the 1st page, then there is no need of First or Previous
 112:         if (currentPage == 0)
 113:         {
 114:             objLbFirst.Enabled = false;
 115:             objLbPrevious.Enabled = false;
 116:         }
 117:         else
 118:         {
 119:             objLbFirst.Enabled = true;
 120:             objLbPrevious.Enabled = true;
 121:         }
 122:
 123:
 124:         //Adding control in a place holder
 125:         plcPaging.Controls.Add(objLbFirst);
 126:         plcPaging.Controls.Add(new LiteralControl("&nbsp; | &nbsp;")); // Just to give some space 
 127:         plcPaging.Controls.Add(objLbPrevious);
 128:         plcPaging.Controls.Add(new LiteralControl("&nbsp; | &nbsp;"));
 129:
 130:
 131:         // Creatig page numbers based on the start and end limit variables.
 132:         for (int i = pageShowLimitStart; i <= pageShowLimitEnd; i++)
 133:         {
 134:             if ((Page.FindControl("lb_" + i.ToString()) == null) && i <= numberOfPages)
 135:             {
 136:                 LinkButton objLb = new LinkButton();
 137:                 objLb.Click += new EventHandler(objLb_Click);
 138:                 objLb.Text = i.ToString();
 139:                 objLb.ID = "lb_" + i.ToString();
 140:                 objLb.CommandName = "pgChange";
 141:                 objLb.EnableViewState = true;
 142:                 objLb.CommandArgument = i.ToString();
 143:
 144:                 if ((currentPage + 1) == i)
 145:                 {
 146:                     objLb.Enabled = false;
 147:                 }
 148:
 149:
 150:                 plcPaging.Controls.Add(objLb);
 151:                 plcPaging.Controls.Add(new LiteralControl("&nbsp; | &nbsp;"));
 152:             }
 153:         }
 154:
 155:         // Last Link button to display with paging
 156:         LinkButton objLbLast = new LinkButton();
 157:         objLbLast.Click += new EventHandler(objLb_Click);
 158:         objLbLast.Text = "Last";
 159:         objLbLast.ID = "lb_LastPage";
 160:         objLbLast.CommandName = "pgChange";
 161:         objLbLast.EnableViewState = true;
 162:         objLbLast.CommandArgument = numberOfPages.ToString();
 163:
 164:         // Next Link button to display with paging
 165:         LinkButton objLbNext = new LinkButton();
 166:         objLbNext.Click += new EventHandler(objLb_Click);
 167:         objLbNext.Text = "Next";
 168:         objLbNext.ID = "lb_NextPage";
 169:         objLbNext.CommandName = "pgChange";
 170:         objLbNext.EnableViewState = true;
 171:         objLbNext.CommandArgument = (currentPage + 2).ToString();
 172:
 173:         //of course if the page is the last page, then there is no need of last or next
 174:         if ((currentPage + 1) == numberOfPages)
 175:         {
 176:             objLbLast.Enabled = false;
 177:             objLbNext.Enabled = false;
 178:         }
 179:         else
 180:         {
 181:             objLbLast.Enabled = true;
 182:             objLbNext.Enabled = true;
 183:         }
 184:
 185:
 186:         // Adding Control to the place holder
 187:         plcPaging.Controls.Add(objLbNext);
 188:         plcPaging.Controls.Add(new LiteralControl("&nbsp; | &nbsp;"));
 189:         plcPaging.Controls.Add(objLbLast);
 190:         plcPaging.Controls.Add(new LiteralControl("&nbsp; | &nbsp;"));
 191:     }
 192:
 193: }
Yes, the code is complex that is why I wrote proper comments which will let you understand the stuff easily.One last thing which is left, is the implementation of dynamically created link button onclick event.
   1: void objLb_Click(object sender, EventArgs e)
   2: {
   3:     plcPaging.Controls.Clear();
   4:     LinkButton objlb = (LinkButton)sender;
   5:     GridView1.PageIndex = (int.Parse(objlb.CommandArgument.ToString()) - 1);
   6:
   7:     managePaging(dt);
   8: }
There it is, we have now completed Custom Paging in Grid View. If you want to download the source code, here is the VS 2008 Solution.Modifications: I have been receiving emails regarding the issues of this post. Especially with the initial five page numbers. I have modified this post to fix the bug it had. Please feel free to point further issues. Also, the download links are also modified.

Datatable to JSON

In this post I will explain you, how can we serialize Datatable to JSON. So that, it can easily pass to JavaScript to get the AJAX done.

First of all fill a Datatable with some results.

DataTable dt = new DataTable();
 
SqlConnection objSqlCon = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["AdventureWorksConnectionString"].ToString());
objSqlCon.Open();
 
SqlDataAdapter objSqlDa = new SqlDataAdapter("select * from Production.Product", objSqlCon);
 
objSqlDa.Fill(dt);

Now create a String Builder object that will contain the JSON text and JavascriptSerializer which will serialize the output in JSON.

StringBuilder objSb = new StringBuilder();
JavaScriptSerializer objSer = new JavaScriptSerializer();

Now here we are going to iterate each row and column of data table and put all of them in Dictionary

Dictionary<string, object> resultMain = new Dictionary<string, object>();
int index = 0;
 
foreach (DataRow dr in dt.Rows)
{
    Dictionary<string, object> result = new Dictionary<string, object>();
 
    foreach (DataColumn dc in dt.Columns)
    {
        result.Add(dc.ColumnName, dr[dc].ToString());
    }
    resultMain.Add(index.ToString(), result);
    index++;
}

Notice that, I have created a new dictionary object for every row and finally put all of the dictionaries in Another dictionary I.E. resultMain.

In the end, I have simply Serialize the resultMain Dictionary to render JSON.

A complete post of utilizing Datatable  in JavaScript through AJAX is in the process, I will post that soon.

Authenticated File Access using HTTP Handler.

In this post I will explain you how authenticate the request directly coming to access a file that is downloadable. some thing like *.pdf or *.zip.

Mostly, people make it working by creating an *.aspx page and then write binary of that file in Response.WriteFile. So, user will have no idea where the file is coming from. now this is the fair approach but what if somebody, somehow know the path of downloadable files.

So, to stop the un authenticated access to our files, we will first create a session enable HTTP handler.

public class MyHttpHandler : IHttpHandler, IReadOnlySessionState
{
 
    public void ProcessRequest(HttpContext context)
    {
        if (context.Session["userId"] == null)
        // I am using a session variable you can also use context.User.Identity.IsAuthenticated
        {
            context.Response.Redirect("/login.aspx?retUrl=" + context.Request.RawUrl);
            //Redirecting to the login page ... alternatively you can also set context.Response.StatusCode 
        }
    }
 
    public bool IsReusable
    {
 
        get { return false; }
    }
}

Now, once we have created that. Let me register my newly creater handler for *.zip and *.pdf files in web.config.

 

<httpHandlers>
  <add verb="*" path="*.zip" type="LearningApp.MyHttpHandler, LearningApp"/>
  <add verb="*" path="*.pdf" type="LearningApp.MyHttpHandler, LearningApp"/>
</httpHandlers>

That’s it. If you want more file types to be authenticated add more verbs in handler section of HttpHandler.

Don’t try to put *.* : That can create some serious problem because then each of your *.aspx, *asmx and all your logic stuff will need authentication.

Disable Special Keys in Win App C#

Today, when I was planning to write an article on Grid View. I got a message from a very good friend of mine who is asking to disable the special keys(Windows Keys) in his application. When I start researching on it, I was thinking that it can be done using e.KeyChar but unfortunately, it is not showing any information about windows keys.

So in this post I will explain you, how can we disable the special keys (in our case windows keys) in C# Application.

1. Crete a c# windows application project
2. On the code behind of your default form add the following references

   1: using System.Diagnostics;
   2: using System.Runtime.InteropServices;

3. Now before the constructor of your form place the following code.

   1: // Structure contain information about low-level keyboard input event
   2: [StructLayout(LayoutKind.Sequential)]
   3: private struct KBDLLHOOKSTRUCT
   4: {
   5:     public Keys key;
   6:     public int scanCode;
   7:     public int flags;
   8:     public int time;
   9:     public IntPtr extra;
  10: }
  11:  
  12: //System level functions to be used for hook and unhook keyboard input
  13: private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
  14: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  15: private static extern IntPtr SetWindowsHookEx(int id, LowLevelKeyboardProc callback, IntPtr hMod, uint dwThreadId);
  16: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  17: private static extern bool UnhookWindowsHookEx(IntPtr hook);
  18: [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  19: private static extern IntPtr CallNextHookEx(IntPtr hook, int nCode, IntPtr wp, IntPtr lp);
  20: [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  21: private static extern IntPtr GetModuleHandle(string name);
  22: [DllImport("user32.dll", CharSet = CharSet.Auto)]
  23: private static extern short GetAsyncKeyState(Keys key);
  24:  
  25:  
  26: //Declaring Global objects
  27: private IntPtr ptrHook;
  28: private LowLevelKeyboardProc objKeyboardProcess; 

4. Now add the following code on your constructor.

   1: public Form1()
   2: {
   3:     ProcessModule objCurrentModule = Process.GetCurrentProcess().MainModule; //Get Current Module
   4:     objKeyboardProcess = new LowLevelKeyboardProc(captureKey); //Assign callback function each time keyboard process
   5:     ptrHook = SetWindowsHookEx(13, objKeyboardProcess, GetModuleHandle(objCurrentModule.ModuleName), 0); //Setting Hook of Keyboard Process for current module
   6:  
   7:  
   8:     InitializeComponent();
   9: }

5. Now Implement the callback function

   1: private IntPtr captureKey(int nCode, IntPtr wp, IntPtr lp)
   2: {
   3:     if (nCode >= 0)
   4:     {
   5:         KBDLLHOOKSTRUCT objKeyInfo = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lp, typeof(KBDLLHOOKSTRUCT));
   6:  
   7:         if (objKeyInfo.key == Keys.RWin || objKeyInfo.key == Keys.LWin) // Disabling Windows keys
   8:         {
   9:             return (IntPtr)1;
  10:         }
  11:     }
  12:     return CallNextHookEx(ptrHook, nCode, wp, lp);
  13: }

6. Now go to your designer class and replace your dispose method.

   1: /// <summary>
   2: /// Clean up any resources being used.
   3: /// </summary>
   4: /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
   5: protected override void Dispose(bool disposing)
   6: {
   7:     if (disposing && (components != null))
   8:     {
   9:  
  10:         components.Dispose();
  11:     }
  12:     if (ptrHook != IntPtr.Zero)
  13:     {
  14:         UnhookWindowsHookEx(ptrHook);
  15:         ptrHook = IntPtr.Zero;
  16:     }
  17:     base.Dispose(disposing);
  18: }

So, in this way we can stop the windows key operation till your application is running.You can find the VS 2008 Source code here.