Thursday, June 2, 2005

Multiple Onclick JavaScript Events on a Single Control

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/multiple_onclick_javascript_events_on_a_single_control.htm]

Change History: This post was updated on July 5, 2005. See Update at bottom.

You can't add two identical events to the same Html control. For example, if you have a button with an onclick event, and you add another onclick at the server, the second event will overwrite the first. This becomes a problem if you need to add an event to some variable control, but you don't know what events that control already has. However, .Net provides a way to handle this.

We can:

  1. See if the variable html control already has an onclick event.
  2. If it does, then dynamically register a new script that calls both the original onclick script as well as the new script.
  3. Replace the variable control's onclick value with that new script.

The following code snippets this. This first block shows a simple JavaScript (with a parameter) being called by a button's onclick event.

function DoStuff1(var1) {
    alert('1: ' + var1);
}

...

...

This snippet shows the server code to check if an onclick event already exists, and add a new wrapper if needed. Note that it persists the onclick values in viewstate to ensure that the wrapper function doesn't wrap itself upon postback. For example, if the first onclick event called DoStuff1(), and we wanted to dynamically add a new onclick function DoStuff2(), we could create a wrapper function Wrapper1() that called both functions, and was called from the onclick. Wrapper1() becomes the new value of the button's onclick.

private void Page_Load(object sender, System.EventArgs e)
{
    string s = this.Button1.Attributes["onclick"];
    if (!Page.IsPostBack)
        OriginalJSFunction = s;
    if (s == null)
        this.Button1.Attributes.Add("onclick","DoStuff2()");
    else
    {
        if (!Page.IsClientScriptBlockRegistered("JS1"))
            Page.RegisterClientScriptBlock("JS1",GetJS(OriginalJSFunction));
        this.Button1.Attributes.Add("onclick","Wrapper1()");
    }
}

private string OriginalJSFunction
{
    get
    {
        object o = ViewState["OriginalJSFunction"];
        if (o == null)
            return "";
        else
            return o.ToString();
    }
    set
    {
        ViewState["OriginalJSFunction"] = value;
    }
}

private string GetJS(string strOriginalFn) //will handle initial JS with params
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    sb.Append(@"
       
    ");

    return sb.ToString();
}

 While this approach is tedious, it lets you dynamically add an event to a control without overwriting that control's existing event. This is useful when making your own custom controls that integrate with existing Html controls already on the page.

UPDATE July 5, 2005

Since writing this post, I've come across a cleaner technique. You can run multiple JavaScripts simply by listing them in the onclick event:

 id="Button1" onclick="DoStuff1('Hello1');Method2();Method3()"

Therefore we can simplify our codebehind. This has two main changes: (1) It modifies OriginalJSFunction to append the ";" to the end of a non-function, (2) It then always adds an onclick method, as opposed to checking if there already exists an onclick method.

private void Page_Load(object sender, System.EventArgs e)
{
  string s = this.Button1.Attributes["onclick"];
  if (!Page.IsPostBack)
    OriginalJSFunction = s;
   
  this.Button1.Attributes.Add("onclick",OriginalJSFunction + ";DoStuff2()");
}

private string OriginalJSFunction
{
  get
  {
    object o = ViewState["OriginalJSFunction"];
    if (o == null)
      return "";
    else
      return o.ToString() + ";";
  }
  set
  {
    ViewState["OriginalJSFunction"] = value;
  }
}

Monday, May 30, 2005

Automating process with Dynamically Compiled C#

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/automating_process_with_dynamically_compiled_c.htm]

I had the opportunity to have another article published in .Net Developer's Journal this month: Automating Your Processes - an NAnt Case Study. The article compares various techniques to automate processes, and then makes a plug for using dynamically compiled C#. This gives the best of both worlds - the flexibility of scripting but the structure and support of a compiled OOP language. The article concludes by applying this to an NAnt build process.

At this time, the article requires a subscription to .Net Developer's Journal to view.

Wednesday, May 25, 2005

Disable Validators on the Client

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/disable_validators_on_the_client.htm]

Sometimes you may want client-side activities to disable a validator. For example, suppose a TextBox is only required if a CheckBox is selected. The TextBox could have a RequiredValidator, but we'd like selecting the CheckBox to enable/disable that validator at the client.

One approach is to use JavaScript functions from the WebUIValidation.js file located at: aspnet_client\system_web\1_1_4322. This stores the script used by ASP.Net's validators and is available to all pages. It includes a script, ValidatorEnable, which takes a control and a boolean and sets the control's Enabled property to the boolean value.

For example, you could have the onclick of a checkbox (id=ChkEnable), call the custom javascript below.

function EnableValidator() {
    var blnEnabled = document.Form1.ChkEnable.checked;
    ValidatorEnable(document.getElementById("RequiredFieldValidator1"),blnEnabled);
}

We still need to persist these changes to the server. We can simply enable/disable the validator in the page load:

this.RequiredFieldValidator1.Enabled = this.ChkEnable.Checked;

In summary, ASP.Net provides easy ways to disable a validator based on client activity. This lets us extend the default validator functionality.

Monday, May 23, 2005

Debugging JavaScript in Visual Studio

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/debugging_javascript_in_visual_studio.htm]

Everyone knows that you can use Visual Studio to debug Class Libraries, such as server-side WebForms. However, you can also use it to debug client-side JavaScript. I've seen some developers who don't realize this because they open up the JS as if it were a class file, add the breakpoint, run the application, and never see the breakpoint hit.

You need to first enable script debugging in your browser (list quoted from MSDN):

  1. In Internet Explorer, click the Tools menu and choose Internet Options.
  2. Click the Advanced tab.
  3. Under the Browsing category, clear the Disable Script Debugging checkbox.

Then any JS file will appear in the Running Documents window. You can open the file from there, add the breakpoint, and step through the script.

Thursday, May 19, 2005

Improving Application Performance by Implementing Paginated Lists

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/improving_application_performance_by_implementing_paginated_lists.htm]

I left CSC and joined Ameripay this week as a Software Engineer. Ameripay is America's leading independent provider of payroll and human resource solutions. While CSC is a great consulting company, I'm excited about the stability (i.e. lack of travel) of e-commerce here. Our dev manager, Chuck Cooper, recently published an article on MSDN: Improving Application Performance by Implementing Paginated Lists.

Most enterprise applications need to page through large sets of data. It's easy to implement this with bad performance. Chuck's article offers high performance ways to handle this, and is a good read.

Wednesday, May 11, 2005

Improving .Net Application Performance and Scalability

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/improving_net_application_performance_and_scalability.htm]

I've been reading a great book, Improving .Net Application Performance and Scalability. While I'm aware of the standard performance issues (minimize calls to the database, reduce postbacks, cache, use client validation, DataReader vs. DataSet, etc...), I've really been impressed with the book so far. It's about a 1000 pages, and is very thorough yet practical.

I admit that I was surprised at the resourcefulness of some of their suggestions, such as how to improve ASP.Net performance by trimming page size:

  • Remove unnecessary white space between tags
  • Avoid long control names (it gets rendered into HTML)
  • Save JavaScript to separate physical files not just for code reuse, but also so that the client can cache it.

They also suggest several tools to help monitor performance:

Monday, May 9, 2005

Using the CLR Profiler

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/using_the_clr_profiler.htm]

In software engineering, our knowledge is limited to what we can test via cause and effect. If we cannot measure the effect of something, it's hard to really know about it. This is one reason why people know relatively less about security and performance than coding "standard" features - it's hard to reliably measure them. However, help is on the way. There exists a free tool, the CLR Profiler, that lets you measure resources in managed code. Resource consumption is linked to performance, so this gives you a tangible way to diagnose some performance-related problems in your .Net App.

Version 2.0 has a great 100 page tutorial. There are other links out there too, such as:

I find this useful to see measurable differences in code snippets. The tutorial shows the difference for using StringBuilder to concatenate strings, but that's just the beginning. For example, the code snippet below opens a file 100 times. If I don't close the reader, I can see the affect of this in the CLR Profiler's "Histogram by Age for Live Objects" chart. In my case, normally the number of objects (less than 34kB) living less than .5 sec was 34KB. If I don't close the reader, it's 800KB.

public static void In_WithClose(string strFileInput)
{
    System.IO.StreamReader sr = null;
    for (int i = 0; i < 100; i++)
    {
        sr = new StreamReader(strFileInput);
        sr.ReadLine();
        if (sr != null)
            sr.Close();
    }
} //end of method

This is a great tool that can be useful for diagnosing memory (and therefore performance) bottlenecks.