Tuesday, February 16, 2010

Get detailed info about all online visitors that are currently browsing your ASP.NET website

In one of our previous answers to very common question that is often asked by beginner developers:
how to show the current number of online visitors for ASP.NET website?
we demonstrated that its really simple to count the number of people that are currently browsing your website.

If you have added this option to your website, at some point of time, you have undoubtedly asked yourself:
"who are those 17 people that are currently browsing my precious website ?" 
"What are their IP addresses? when did they come here, what page they first opened and from where did they came from?"

Yes i know you can see that information later by examining the WebSitelogs, but having the current information is something that no laterexamination of the logs can compensate.

The trouble is that there is no standard way to enumerate active visitor sessions on your ASP.NET website.
You can only enumerate keys and values of the current HTTPContext Session for each user but this is not enough.
This is caused by the builtin ASP.NET security measure that forbids individual user sessions to access other sessions.

(If this would not be forbidden, many bad things could happen to you while you browse a website, so this is a good thing).

But if there is no easy way to get the list of all current user sessions, that does not mean we cannot track them.

Here is what we are going to do:

We will create a global collection that will hold the list of allactive user sessions, and as each new user hits our website we will addhis/hers information to this list, also when any of the sessionexpires, we will remove its information from this global list.

First we must setup our WebSite session configuration in web.config like this:

    <sessionState mode="InProc" cookieless="false" timeout="30"/>

This will tell the ASP.NET runtime to create a session in the memory ofthe Web Server for each visitor and keep it alive for 30 minutes.
If user keeps browsing our website and opens some of the pages every few minutes, his session is kept alive.
But if he does not open any new pages on our website for 30 or more minutes, his session will expire.

Now how do we know when a new session is created or when it expires?
This is why ASP.NET is great: you can add to your website Global.asax class (file) where you can easily add code to handlers for some very important application-level events.

(To create a new Global.asax class file in your ASP.NET website,right-click on the website in the Solution Explorer, and choose options:

ADD NEW ITEM->GLOBAL APPLICATION CLASS).

There are number of application events that you can handle in thisclass, but in this case, events that are important for us are Session_Start and Session_End.

Session_Start event handler code is fired when new visitor comes to ourwebsite, and Session_End is fired for each visitor if he does not openany pages on our website longer than our TIMEOUT setting insessionState configuration of web.config file.

NOTE: ASP.Net runtime hasno other way of knowing if user has left our website. If user opens onepage, and then immediately closes his web-browser, this will nottrigger the Session_End event at once. This event will eventuallytrigger when the Timeout timer elapses.

Now that we know how to hook into the process of creating anddestroying visitor sessions, we need to define a simple WebsiteVisitorobject that we will create for each visitor to store information abouthim and keep it in our global collection.

Here is the definition of WebsiteVisitor class:

namespace
Roboblob.Utility
{
    public class WebsiteVisitor
    {
        private string sessionId;
        public string SessionId
        {
            get { return sessionId; }
            set { sessionId = value; }
        }

        public string IpAddress
        {
            get { return ipAddress; }
            set { ipAddress = value; }
        }
        private string ipAddress;

        public string UrlReferrer
        {
            get { return urlReferrer; }
            set { urlReferrer = value; }
        }
        private string urlReferrer;

        public string EnterUrl
        {
            get { return enterUrl; }
            set { enterUrl = value; }
        }
        private string enterUrl;

        public string UserAgent
        {
            get { return userAgent; }
            set { userAgent = value; }
        }
        private string userAgent;

        public DateTime SessionStarted
        {
            get { return sessionStarted; }
            set { sessionStarted = value; }
        }
        private DateTime sessionStarted;



        public WebsiteVisitor(HttpContext context)
        {
            if  ((context != null) && (context.Request != null) && (context.Session != null) )
            {
                this.sessionId = context.Session.SessionID;
                sessionStarted = DateTime.Now;
                userAgent = string.IsNullOrEmpty(context.Request.UserAgent) ? "" : context.Request.UserAgent;
                ipAddress = context.Request.UserHostAddress;
                if (context.Request.UrlReferrer != null)
                {
                    urlReferrer = string.IsNullOrEmpty(context.Request.UrlReferrer.OriginalString) ? "" : context.Request.UrlReferrer.OriginalString;
                }
                enterUrl = string.IsNullOrEmpty(context.Request.Url.OriginalString) ? "" : context.Request.Url.OriginalString;
            }
        }


    }

}

As you can see its very simple object with some public properties for the data we want to track about our visitor.

In the constructor of the object we pass current HttpContext object asthe parameter that will allow our object to extract all the data needed.

In this example we track the visitors IP Address, SessionID, BrowserUser Agent string, Date and Time when user entered our website, FirstPage he opened, and Referrer Url from which he visited us (If any).

Here you can add some other interesting data if you can retrieve it. Its up to you.

Now let us create a global collection where we will keep our active WebsiteVisitor objects:

To make it available to all of our pages, we will put it in publicstatic class in the same file where we placed WebsiteVisitor object:

    public static class OnlineVisitorsContainer
    {
      public static Dictionary<string, WebsiteVisitor> Visitors = new Dictionary<string,WebsiteVisitor>();
    }


As you can see its a generic Dictionary class, where keys (that areused to store and retrieve values from the dictionary) are of string type, while the values are of WebsiteVisitor type.



We have declared our container class OnlineVisitorsContainer and Dictionary collection  Visitors  as static.

This way the container class cannot be instantiated, and therecan be only one instance of Visitors collection to be shared amongstall of our pages and code.

Now you are probably thinking: what we will use as a string key for each Visitor?

Well, this is a tricky question.

The most logical answer is to use the SessionID that is anyway createdfor each visitor as unique identifier for his session data.

Now that we solved this dilemma, lets us move to the code in the event handlers in Global.asax file:

First add these lines to top of your Global.asax file to import namespaces that we will use:

<%@ Import Namespace="System.Collections.Generic" %>
<%@ Import Namespace="Roboblob.Utility" %>

Here is the code for the application event that gets fired when sesion is started (when visitor comes to our website):

    void Session_Start(object sender, EventArgs e)
    {
        // add some data to the Session so permanent SessionId is assigned
        // otherwise new SessionId is assigned to the user until some data
        // is actually written to Session
        Session["Start"] = DateTime.Now;

        // get current context
        HttpContext currentContext = HttpContext.Current;

        if (currentContext != null)
        {
            if (!OnlineVisitorsContainer.Visitors.ContainsKey(currentContext.Session.SessionID))
            {
                WebsiteVisitor currentVisitor = new WebsiteVisitor(currentContext);               
                OnlineVisitorsContainer.Visitors.Add(currentVisitor.SessionId, currentVisitor);
            }
        }
    }

So in the event handler that is fired when session is created, we firstplace current Date and Time in the current session so that permanentSessionID is assigned to the Session.

If we do not do that, it can happen that new SessionID is created forthe same user again and again until something is actually written tothe Session collection.

Afterwards, we get the reference to current HttpContext and pass it to the constructor of the new WebsiteVisitor object and add this object to the global Visitors collection.

Now to the Session_End event handler code:

    void Session_End(object sender, EventArgs e)
    {
        // Code that runs when a session ends.
        // Note: The Session_End event is raised only when the sessionstate mode
        // is set to InProc in the Web.config file. If session mode is set to StateServer
        // or SQLServer, the event is not raised.

        if (this.Session != null)
            OnlineVisitorsContainer.Visitors.Remove(this.Session.SessionID);
    }

In Session_End event handler we cannot access current HttpContextinstance, but we can access current Session and therefore we canretrieve current SessionID and use it to remove current visitorssession from the our global collection of online visitors.

We have finished most of our work. We have implemented all thenecessary code that will keep our global collection updated, and itwill always hold the list of the visitors that are currently browsingour website.

Lets add GridView and some code to Default.aspx page that will show our active website visitors:


    <asp:GridView ID="gvVisitors" runat="server" AutoGenerateColumns="False"
        CellPadding="2" ForeColor="#333333" GridLines="Both" >
        <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
        <RowStyle BackColor="#EFF3FB" />
    <Columns>
        <asp:TemplateField HeaderText="Session Started">
        <ItemTemplate>
            <%# ((DateTime)Eval("SessionStarted")).ToString("dd/MM/yyyy HH:mm:ss") %><br />
        </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Ip">
        <ItemTemplate>
            <%# Eval("IpAddress") %>
        </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Other">
        <ItemTemplate>
            <span style="font-size:small;">
            <%# Eval("UserAgent") %><br />
            <%# Eval("EnterUrl") %><br />
            </span>

            <asp:HyperLink ID="refurl" Text='<%# Eval("UrlReferrer") %>' Font-Size="Small"
            NavigateUrl='<%# Eval("UrlReferrer") %>' runat="server" Target="_blank" />
        </ItemTemplate>
        </asp:TemplateField>

    </Columns>
        <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
        <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
        <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
        <EditRowStyle BackColor="#2461BF" />
        <AlternatingRowStyle BackColor="White" />
    </asp:GridView>

And now we must only bind our global visitors collection from OnlineVisitorsContainer class to the GridVIew control:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
            if (OnlineVisitorsContainer.Visitors != null)
            {
                gvVisitors.DataSource = OnlineVisitorsContainer.Visitors.Values;
                gvVisitors.DataBind();
            }
        }
    }
Open your webpage in your browser (for example first do it in Firefox)and you will see single visitor displayed in GridView. To test if itworks, fire up another browser (different one maybe Internet Exploreror Opera) and refresh the page. There should be two visitors shown inthe list.

If you dont click on any pages for 30 minutes in one of the browsers it will eventually disappear from the list.

here is how it should look:

Prevent Form Submission When User Presses the ENTER Key in TextBox

All we need to do to is to add JavaScript code to the OnKeyDown event of the TextBox instance that will prevent

Form submission if ENTER key is pressed.

Just add this code to your Page_Init method of the page where you want this behavior:

    protected void Page_Init(object sender, EventArgs e)
    {
        TextBox1.Attributes.Add("onkeydown",
        "if(event.which || event.keyCode){if (event.keyCode == 13) return false;}");
    }

Your TextBox will continue to work like before, but if user presses ENTER key, your
Web Form will not be submitted.

Programmatically add JavaScript File to Asp.Net page

If you do not want to add your JavaScript files declaratively via HTMLto your ASP.Net page, you can do it in code, and here is how:

Just add this code to the Page_Init event handler on your page:

    protected void Page_Init(object sender, EventArgs e)
    {
        HtmlGenericControl js = new HtmlGenericControl("script");
        js.Attributes["type"] = "text/javascript";
        js.Attributes["src"] = "jscript/formfunctions.js";
        Page.Header.Controls.Add(js);
    }
And thats all.

How to determine which Control caused PostBack on ASP.NET page?

Very often we have multiple parts of page that can submit some user information to the server.

We can easily divide parts of our WebForm with ValidationGroups intological parts that we can validate before the data is submitted.

But if we have on our page more than one ASP.Net Control that can causepostback to the server (for example if we have multiple Buttons,DropDownLists, ImageButtons etc) how can we determine which one of themuser clicked to submit the page to the server?

To precisely determine this important bit of information we need adetailed overview on what happens when the WebForm is submitted to theserver:

If you lookup the HTML code that ASP.Net generates when you put Form on your page you will find hidden input field named
"__EVENTTARGET"

This is where ASP.Net stores the name of the Control that initiated the postback of the WebForm.

Having the name of the control, we can easily get the reference to theinstance of that control via FindControl method of the Page object.

But not always :)

There are two exceptions to this simple rule: when Button and ImageButton controls are clicked on the form.

We need to handle those two cases differently:

Handling the Button control:

If a Button control was clicked our field "__EVENTTARGET" is not assigned.

The only thing we can do to find out the name of the control thatsubmitted the form is to loop trough all the names of the variables inPage.Request.Form collection that is filled for us, and the only onethat is the instance of ASP.Net Button class is the Button that causedthe postback. This is becasue if Button is causing the postback, theonly Button control value that is sent to the server is the one thatcaused the postback.
Other Button controls are simply ignored and not sent to the server. Is that cool or what?

Handling the ImageButton control:

If image button control was clicked to submit the form, very similar sequence of events happen:

Only the ImageButton control that caused the postback is sent to the server, but with a slight difference:

Instead of one variable in this case two form variables are created forImageControl that caused the postback (and stored in Page.Request.Formcollection).

First variable is called the same as ImageControl only with ".x" suffixand the value of that form variable is the relative horizontal positionof the mouse pointer inside the ImageControl at the time when it wasclicked.

Second variable that is created is named same as the ImageControl but with suffix ".y" and its value is the relative vertical position of the mouse pointer inside the ImageControl at the time when it was clicked.

So if user clicked the top left corner of the ImageControl these variables will both have values 0.

For us it is important to know that first variable submitted that has suffix of ".x" or ".y" isthe name of the ImageButton control that caused postback (with suffix)and then we can easily strip the suffix to get the actual name of theImageButton control.

Note: this will only work if you dont name some other controls on the WebForm with ".x" or ".y" suffix so be careful with that!

Ok here is the method that we can use to retrieve the instance of the control that caused the postback:

    public static Control GetPostBackControl(Page page)
    {
        Control postbackControlInstance = null;

        string postbackControlName = page.Request.Params.Get("__EVENTTARGET");
        if (postbackControlName != null && postbackControlName != string.Empty)
        {
            postbackControlInstance = page.FindControl(postbackControlName);
        }
        else
        {
            // handle the Button control postbacks
            for (int i = 0; i < page.Request.Form.Keys.Count; i++)
            {
                postbackControlInstance = page.FindControl(page.Request.Form.Keys[i]);
                if (postbackControlInstance is System.Web.UI.WebControls.Button)
                {
                    return postbackControlInstance;
                }
            }
        }
        // handle the ImageButton postbacks
        if (postbackControlInstance == null)
        {
            for (int i = 0; i < page.Request.Form.Count; i++)
            {
                if ( (page.Request.Form.Keys[i].EndsWith(".x")) || (page.Request.Form.Keys[i].EndsWith(".y")))
                {
                    postbackControlInstance =page.FindControl(page.Request.Form.Keys[i].Substring(0,page.Request.Form.Keys[i].Length-2) );
                    return postbackControlInstance;
                }
            }
        }
        return postbackControlInstance;
    }   

And here is an example on how to use that method in our Page:

    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            Control cause = GetPostBackControl(Page);
            // use the control, typecast it etc
        }
    }

Control ASP.NET Validator Controls Client Side validation from JavaScript

Recently I needed to disable some ASP.NET RequiredFieldValidator controls on one page depending on user input.

This is trivial you say. But the problem was that this needed to occurinstantly on Client Side based on the state of one CheckBox control andnot after a full page PostBack.
For example imagine you have TextBoxcontrol that needs to be filled by user only if one CheckBox control onthe page is checked.

So normally, you would add RequiredFieldValidator to the page, wire itup with that TextBox control, and enable/disable this Validator onPageLoad event depending on the Checked state of the CheckBox.
But let us consider this rare situation:User checks the CheckBox, without filling the TextBox and submits thepage. On PageLoad the RequiredFieldValidator gets activated, and pageis presented to the user again with validation error.

User changes his mind and makes Checkbox unchecked (he does not want tofill the TextBox) and tries to submit the form, but what happens?

Client Side validation of RequiredFieldValidator is triggered toenforce user to fill that TextBox and user cannot submit the page.
The only solution is to Enable/Disablethe ASP.NET Validator controls on page with JavaScript code as soon asuser changes the Checked state of the CheckBox control on the page.
After some digging I found out that ASP.NET Validator controls have Client Side API that supports some niffty features, so here is the list of supported Client Side functions:


ValidatorValidate(val)

Takes a client-validator as input. Makes the validator check its input and update its display.

ValidatorEnable(val, enable)

Takesa client-validator and a Boolean value. Enables or disables a clientvalidator. Being disabled will stop it from evaluating and it willalways appear valid.

ValidatorHookupControl(control, val)

Takesan input HTML element and a client-validator. Modifies or creates theelement's change event so that it updates the validator when changed.This can be useful for custom validators that depend on multiple inputvalues.
 NOTE:
One thing is important to say here: ServerSide Validation will occur after page PostBack even if youprogrammatically disable Client Side validation with Java Script.
This API just allows you to manipulatethe Client Side state of your Validator controls, Disable or Enablethem and therefore allow or forbid the user to submit the page, and allthis does not affect how this Server Side Validators will behave onServer Side.
So how we use that API?



Let us set up a simple example project with two TextBox controls (FullName and Email) with RequiredFieldValidators for both of them andRegularExpressionValidator for Email field and a CheckBox control thatEnables/Disables the Client Side validation for all the Validatorcontrols.



Here is how our ASPX page code looks like:

 
<body onload="InitValidators();">
    <form id="form1" runat="server">
    <div>
        <asp:CheckBox ID="enableValidatorsCheckBox" runat="server" Text="Validators Enabled - Click on this checkbox to Enable/Disable client side validation for ASP.NET Validator controls." Checked="true" onclick="ValidatorsEnabled(this.checked);" />
        <br /><br />
        <asp:Label ID="Label1" runat="server" Text="Full Name"></asp:Label>&nbsp;
        <asp:TextBox ID="Name" runat="server"></asp:TextBox>
        <asp:RequiredFieldValidator ID="NameRequiredFieldValidator" runat="server" ControlToValidate="Name"
            ErrorMessage="Please enter your full name"></asp:RequiredFieldValidator>
        <br />
        <asp:Label ID="Label2" runat="server" Text="Email"></asp:Label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <asp:TextBox ID="Email" runat="server"></asp:TextBox>
        <asp:RequiredFieldValidator ID="EmailRequiredFieldValidator" runat="server" ControlToValidate="Email"
        ErrorMessage="Please enter your email address."></asp:RequiredFieldValidator>
        <asp:RegularExpressionValidator ID="EmailRegexValidator" runat="server"  ControlToValidate="Email"
        ErrorMessage="Invalid Email" ValidationExpression=".*@.{2,}\..{2,}"></asp:RegularExpressionValidator>
        <br /><br />
        <asp:Button ID="SubmitButton" runat="server" Text="Submit" />   
        <br /><br />
        <asp:Label ID="MessagesLabel" runat="server" Text=""></asp:Label> <br />
        <br />
        <asp:ValidationSummary ID="ValidationSummaryControl" runat="server"
            HeaderText="Validation Errors:" /> 
    </div>
    </form>
    <script type="text/javascript" language="javascript">
        function InitValidators()
        {
            // retrieve instance of our checkbox
            var checkbox = document.getElementById('<%=enableValidatorsCheckBox.ClientID%>');
            // enable/disable all validators on page based on checkbox state
            ValidatorsEnabled(checkbox.checked);
        }

        function ValidatorsEnabled(state)
        {
            ValidatorEnable(document.getElementById('<%=NameRequiredFieldValidator.ClientID%>'), state);
           ValidatorEnable(document.getElementById('<%=EmailRequiredFieldValidator.ClientID%>'), state);                                   
            ValidatorEnable(document.getElementById('<%=EmailRegexValidator.ClientID%>'), state);           
        }
    </script>
</body>


So as you can see from the code we have created a simple helper Java Script function ValidatorsEnabled(state) to enable/disable all validator controls on the page depending on the state parameter supplied.



On each page load (using the body onload Client Side event) we call the InitValidators() function that just takes the Checked state of the CheckBox control and calls the ValidatorsEnabled with that value so that Client Side validation is enabled/disabled on page load.
Also whenever user clicks on the CheckBox control (using the onclick Client Side event) we call the ValidatorsEnabled function also supplying the state of the CheckBox as state parameter to Enable/Disable Client Side validation.
Note: we are using Server Side tags in our JavaScript code: '<%=NameRequiredFieldValidator.ClientID%>' todynamically retrieve the Client Side names of our Server SideValidators so we don't have to hard code them, and therefore have tochange them in Java Script code if we change their names on Server Side.



And here is what happens at our code behind:

 
    protected void Page_Load(object sender, EventArgs e)
    {
        if (Page.IsPostBack)
        {
            Page.Validate();

            StringBuilder msg = new StringBuilder("Page Is Submitted. Validation State: ");

            if (Page.IsValid)
            {
                msg.AppendFormat("Valid. Submitted Name: {0}  Submitted Email: {1}", Name.Text, Email.Text);
            }
            else
            {
                msg.Append("INVALID!");
            }

            MessagesLabel.Text = msg.ToString();
        }
    }


Simple stuff here, we just trigger the page Validation if there is aPostBack and display status messages in the Label control depending onthe validation status of the page/validation controls.
So let us see what happens if we runthe sample page? Initially Client Side validation is enabled for allour Validator controls and until we fill the required data we cannotsubmit the page:





If you try to submit this page Post Back will not occur because Client Side validation will get triggered.
Here is the result, page is notsubmitted, instead an error message is displayed in each Validatorcontrol and in Validation Summary control on the bottom of the page:





Now, if you click on the CheckBox on topand uncheck it, the Client Side validation for all our Validatorcontrols will be instantly disabled by calling our Java Script functionValidatorsEnabled(false) and all the validation errors will at once dissapear!
You will be permitted to submit thepage, even if you dont fill in the required information, full pagePostBack will happen and on the Server Side validation will gettriggered for each Validator control.

Good side of this is that you canoverride the Validators behaviour on Client Side and allow the PostBack to occur and then decide on Server Side if you really want todisable some of the Validator Controls or display error messages,enforce custom business logic etc.


Here is how the page looks after you have disabled Client Sidevalidation and submitted it without filling all the required data:




TIP:
Always make sure you have a really goodreason to do all this because generally Client Side validation is agood thing and should be enabled by default and used whenever possiblebecause it saves bandwidth and forbids unnecessary postbacks.



Control is now in your hands, so use it wisely :)



Sample Visual Studio WebSite Code Download

Thursday, February 11, 2010

Open popup windows in IE/Firefox and return values using ASP.NET and Javascript

With the forums flooded with questions of opening a popup window, passing values to the popup window and then return values back to the parent page using both Internet Explorer and Firefox, I decided to take a plunge into the subject and experiment with an easy implementation. This article explains how to transfer values between the Parent page and a Pop-up window. The code has been tested against IE7 and Firefox.
Internet Explorer(IE) contains the showModalDialog() method which proves very effective while creating a modaldialog box and displaying a html document in it. One caveat being,showModalDialog() is not a W3C implementation. So it is not supportedin Firefox (as of version 2.0.0.11). Firefox supports the window.open()method. However there is no built in functionality in Firefox thatkeeps the popup modal. At the most, you can use ‘modal=yes’ to make the popup appear in front. However unlike the showModalDialog() in IE, you cannot restrict the user to access the parent page when the popup is opened in Firefox. In short, it is not truly modal. There are different ways to get around this problem, however in this article, we will only focus on exchanging values between the parent and popup page.
In this article, we will see how to take a simple approach and create a popup window using both IE and Firefox. In the first part, we will pass in the first name from the parent page to the popup window. In the second part, the popup window will reverse the name and return there versed string to the parent window. All set!! Let us get started.
Part 1 - Passing value to Popup window
Step 1: Open Visual Studio. Create a new website (File > New > Website). Set the location, filename and language of the project.
Step 2:In your Default.aspx, add a label (lblFName), textbox (txtFName) and abutton (btnPopup). Set the ‘Text’ property of the label to ‘FirstName’.Similarly set the ‘Text’ property of the button control to ‘Show Popup’.
Step 3:Now add the popup form. Right click your project > Add New Item >Web Form > Rename form as ‘PopupWin.aspx’ and click on Add.
Step 4: In the PopupWin.aspx, add a textbox (txtReverse) and a button (btnReverse).
Wellnow we have two pages, Default.aspx which is the parent page andPopupWin.aspx which will be the popup page. Let us now pass a valuefrom Default.aspx to the popup window.
Step 5:We will invoke the popup window on the button (btnPopup) click ofDefault.aspx. To do so, we will use Button.Attribute.Add and call ajavascript function that will open the popup window. The javascriptfunction will be contained in a seperate pop.js file which we willcreate shortly. Add this code in the code behind file of yourDefault.aspx.
C#
protected void Page_Load(object sender, EventArgs e)
    {
        btnPopup.Attributes.Add("onClick", "javascript:InvokePop('" + txtFName.ClientID + "');");
    } 
VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        btnPopup.Attributes.Add("onClick", "javascript:InvokePop('" & txtFName.ClientID & "');")
    End Sub
Over here we are passing the ClientID of the textbox. ClientID is the identifier of the server control, generated by ASP.NET. You must be wondering why I am not passing the value of the textbox directly. Well passing the control has an advantage where there is more than one control that is passed to the popup page. While returning back the values from the popup to the parent page; it helps you to decide and determine which control receives which value. Even though we will be using only one textbox for simplicity, I thought of creating a sample which can be extended later by you to suit your needs. If the use ofClientID is not clear to you, wait till we get to part 2 of thisarticle, and I will again touch upon the subject.
Step 6:Let us now create the javascript functionality which will open thePopup. Right click your project > Add New Item > Jscript file> Rename the file to pop.js. Add the following function to thepop.js file
function InvokePop(fname)
{
        val = document.getElementById(fname).value;
        // to handle in IE 7.0          
        if (window.showModalDialog)
        {      
            retVal = window.showModalDialog("PopupWin.aspx?Control1=" + fname + "&ControlVal=" + val ,'Show Popup Window',"dialogHeight:90px,dialogWidth:250px,resizable:
yes,center:yes,");
            document.getElementById(fname).value = retVal;
        }
        // to handle in Firefox
        else
        {     
            retVal = window.open("PopupWin.aspx?Control1="+fname + "&ControlVal=" + val,'Show Popup Window','height=90,width=250,resizable=yes,modal=yes');
            retVal.focus();           
        }         
}
This function accepts the textbox control, retrieve’s the value of the textbox that needs to be reversed and passes the textbox control and its value to PopupWin.aspx through query string. This is the function which will be called on the btnPopup click.
Step 7: To wire up the .js with your asp.net pages, add a link to the javascript file in both the pages as shown below:
Default.aspx
<head runat="server">
    <title>Parent Page</title>
    <script type="text/javascript" src="pop.js"></script>
</head>
PopupWin.aspx
<head runat="server">
    <title>Popup Page</title>
    <script type="text/javascript" src="pop.js"></script>
</head>
Step 8:In the code behind file of PopupWin.aspx, add the following code at the Page_Load() to retrieve the value from the querystring and display the value in the TextBox ‘txtReverse’, placed in the popup window.
C#
protected void Page_Load(object sender, EventArgs e)
    {
        txtReverse.Text = Request.QueryString["ControlVal"].ToString();
    }
VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        txtReverse.Text = Request.QueryString("ControlVal").ToString()
    End Sub
If you are eager to test the value going from Parent page to the popupwindow, you can do so now. Make Default.aspx as ‘Set as Start page’ andrun the sample. Enter your name in txtFName TextBox and click on ShowPopup button. A popup window opens with the value entered in the Parentpage.
Part 2 - Passing value from Popup window to the Parent page
In this second part, we will reverse the string and pass the reversed string back to the parent page. To do so, follow these steps:
Step 1: Add additional functions to the pop.js file which will reverse the string and return the string back to the parent page.
// pop.js
function ReverseString()
{
         var originalString = document.getElementById('txtReverse').value;
         var reversedString = Reverse(originalString);
         RetrieveControl();
         // to handle in IE 7.0
         if (window.showModalDialog)
         {             
              window.returnValue = reversedString;
              window.close();
         }
         // to handle in Firefox
         else
         {
              if ((window.opener != null) && (!window.opener.closed))
              {              
                // Access the control.       
                window.opener.document.getElementById(ctr[1]).value = reversedString;
              }
              window.close();
         }
}
function Reverse(str)
{
   var revStr = "";
   for (i=str.length - 1 ; i > - 1 ; i--)
   {
      revStr += str.substr(i,1);
   }
   return revStr;
}
function RetrieveControl()
{
        //Retrieve the query string
        queryStr = window.location.search.substring(1);
        // Seperate the control and its value
        ctrlArray = queryStr.split("&");
        ctrl1 = ctrlArray[0];
        //Retrieve the control passed via querystring
        ctr = ctrl1.split("=");
}
As you saw in part 1, the value was passed from the parent window to the popup window and was kept in the txtReverse TextBox. The functionReverseString() retrieves the value from this textbox and passes the string to the Reverse() function which reverses the string. There versed string is then kept in the ‘reversedString’ variable. The ‘RetrieveControl’ splits the query string and identifies the control in the parent page to which the reversed string value is tobe sent.
Note:If you observe, in the IE implementation, I am not really making use of the RetrieveControl(), however in Firefox I am. If you remember, in theb eginning of part1 , I had mentioned the use of ClientID, using which both controls and their values can be passed to determine which control recieves which value. This is especially needed when there are multiple controls. Well the RetrieveControl seperates the different controls and you can use the variables in this method to return values to there spective contro.l
The value is then returned to the parent window and the popup window is closed.
Step 2:Now in order to use these newly added javacript functions, just call the ReverseString() function on the btnReverse click. To do so, add the onclick attribute to the btnReverse.
<input class="button" type="button" id="btnReverse" value="Reverse value back" onclick="ReverseString();"/>
That’s it. Now test the code. Pass your name from the Parent window to the Popup window and then reverse the string and pass it back to the Parentwindow.
I would like to mention that there are multiple ways of doing the task demoed in this article. I have seen some cool examples by experts. One of them I would like to mention is that of NC01where he makes use of properties and ViewState, to pass values to the Popup window. I would encourage you to use this article as a base and ry out your own implementations that would work on multiple browsers.
The source code of this article, both in C# and VB.NET, can be downloaded from here. I hope this article was useful and I thank you for viewing it.