Tuesday, December 13, 2011

Enhancing Telerik's RadListBox

As a follow-up to the previous post, I wanted to share the work I've done to add server-side functionality via client-side calls.  Though this example focuses heavily on Telerik controls, what I've developed is completely generic ASP.net code that can be used in any situation where one needs to cause a server-side event but one is not natively available from the control.
The basic challenge I was facing was to allow both single & double clicking in Telerik's RadListBox.  Natively, single-clicks are handled on the server via: OnSelectedIndexChanged.  But there is no server-side equivalent for double-clicks.

Here's what happens when the RadListBox is single-clicked. Eventually a lot more will occur, including a flowchart appearing, unique to each selected item.

When the RadListBox is double-clicked, a popup dialog appears.  In this case it's a RadWindow:

Here's the ASP.Net layout code:


Here's the client-side Javascript, which initially makes heavy use of jQuery to obtain the ListBox object. The key points to note are:
  1. The use of the clickTimer variable to help distinguish between single & double clicks.
  2. The differentiation of click type via the initial parameter of either "1" or "2".
  3. Forcing a server-side postback via ASP.Net's __doPostBack Javascript function.
  4. The first parameter used with __doPostBack is the ID of the AJAX UpdatePanel.  Doing so ensures that a partial, rather than full, postback occurs, which is much more aesthetically pleasing. 

Finally, here's the server-side code that handles the postback forcibly caused by the client. The key points to note are:
  1. The initial checking of the  __EVENTTARGET system variable is very rudimentary.  If multiple such client postbacks existed from different controls then more sophisticated checking could be accomplished..
  2. What parameters are passed is entirely up to the developer.  In this example, passing just the "1/2" and the ListBox index was sufficient. 
    protected void Page_Load(object sender, EventArgs e)
      if (!Page.IsPostBack)
        // The code in this construct strictly handles the single & double clicking of the RadListBox
        if (this.Request["__EVENTTARGET"].EndsWith("UpdatePanel1"))
          string parameters = this.Request["__EVENTARGUMENT"];

          if (parameters != "")
            // This event may have been fired by either a single-click or a double-click.  We need to 
            // find out which one.  The first parameter will be either "1" or "2", indicating which.
            string[] sParamArray = parameters.Split(new char[] { ',' });

            int idx = Convert.ToInt32(sParamArray[1]);
            if (sParamArray[0] == "1") // Single-click

Saturday, December 10, 2011

Retrieving a Nested ASP.Net Object with Javascript & jQuery

Though I consider myself to be an accomplished C# / ASP.Net web developer, one of my weaknesses lies with client-side Javascript programming. I don't like the free-form, type unsafe nature of it and firmly believe that long term maintainability of Javascript code is a nightmare. The introduction of jQuery has improved things somewhat but there are still times when things can get very tricky. One prime example involves working with nested ASP.Net controls. Here's a quick example of such a situation:

What you're seeing in this image are a number of Telerik controls (which I highly recommend!) that provide a superb user experience for the application I'm building.
My task at hand was to add some custom client-side code for when an item in the listbox was double-clicked.

Here's a snippet of the layout code for the page:

The challenge was to obtain the ListBox from within the nested objects. Here's a sample Javascript function, that utilizes jQuery, to retrieve the RadListBox object:
 function rlbDoubleClicked() {
   var slidingZone = $find($('table[id$=_radSlidingZone]')[0].id);
   var slidingPane = slidingZone.getPanes()[0];

   // Standard Approach
   var listBox = $find($("#" + slidingPane.get_id() + " #<%= radListBoxStopes.ClientID %>")[0].id);

   // Alternate Approach (necessary if the function is to be placed in an external Javascript file)
   var listBox = $find($("#" + slidingPane.get_id() + " div[id$=_radListBoxStopes]")[0].id);
Hopefully this example will help others with their own projects!

Saturday, January 22, 2011

How to Prevent Visual Studio from Timing Out While Debugging an ASP.net Application

For some time now I've experienced the annoying phenomenon of Visual Studio timing out very quickly during a debugging session. I was never able to precisely nail down exactly how long the timeout duration was but it seemed like a couple of minutes.

I'd searched for a solution to this problem before but could never find one. Today I did. Unfortunately the author wasn't very verbose about how to find specific items referred to in the solution so I thought I'd do so here in the hopes that it will help others.

Note: Much to my disappointment, this only works if you get VStudio to attach to IIS.  I have not yet found a way to configure the internal Web Development Server in a similar fashion.  :-(

For starters, on my workstation I am running Windows 7 Ultimate and Visual Studio 2008.  The aforementioned posting refers to changing a timeout value in Internet Information Services (IIS) 7.  I didn't have it enabled on my computer (only on a server) so the first thing I did was install it:
  1. Start → Control Panel → Programs and Features → Turn Windows features on or off (left column)
  2. In the dialog box that appears, simply check "Internet Information Services" and press OK.
  3. After about a minute, it appeared to have installed the necessary components, though no new options showed up in the Control Panel nor in the Administrative Tools section of the same.
  4. With no explicit way to run the IIS Manager, I went to Start and then typed the following into the Search textbox:  inetmgr
  5. To make it more readily available in the future I right-clicked on the "inetmgr" item that appeared from the search and chose "Open file location".
  6. I located "InetMgr.exe" in this folder and dragged & dropped a shortcut into the "Administrative Tools" folder (just hold down Shift + Ctrl to force a shortcut to be created).
  7. For security reasons it wouldn't let me but did ask if I wanted a shortcut placed on the Desktop.  Yes!
  8. I then moved the new shortcut into the Administrative Tools folder.  Other than a confirmation prompt, it let me do it.
  9. Finally, I renamed "InetMgr.exe" to "IIS Manager", confirming a prompt to finalize the name change.
Then I opened up the IIS Manager.  From the aforementioned posting it wasn't immediately obvious what to do.  But after a little trial & error I figured it out:
  1. Double-click on "Configuration Editor" (bottom row, left side).
  2. In the left column click on "Application Pools".
  3. In the center column click on the row beginning with "DefaultAppPool".  On my system it was the only one present.
  4. In the right column click on "Advanced Settings..."
  5. In the dialog box that appears scroll down a little to the "Process Model" section.
  6. You can either changed "Ping Enabled" to False to have an unlimited timeout or you can change the value of "Ping Maximum Response Time (seconds)" from the default of 90 to something else.  I opted for 900 seconds.

Tuesday, January 11, 2011

Input Parameters: One Better than Strings and Enums

The ASP.Net/C# project I'm working on has several methods that resemble this general pattern:
  public void UpdateDataTable(DataRow dataRow, object objFldName, object newValue)
    string fieldName = objFldName.ToString();
In times past the 2nd parameter would always be string fieldname but to increase data typing and reduce errors I started introducing Enums wherever possible.  In this particular example UpdateDataTable processes DataRows from several different DataTables.  As I have a different Enum for each DataTable's fieldnames and not one Enum for all DataTable fieldnames, a solitary Enum parameter type isn't possible.

I eventually realized that I could use the little trick of specifying a generic object data type and then convert each parameter value over to its string equivalent.  A simple trick, yes, but also an effective one!