Monday, October 1, 2007

A custom StateManagedCollection Vs a Generic List

So, you have a collection class and you want it to maintain state. In the past versions of asp.net, we had to do this manually, managing state in our collection ourselves. It was not a difficult task but it required a bit of extra work. Now in asp.net 2.0 release, we dont need to do this ourselves anymore. Instead what we can do is take advantage of StateManagedCollection class.

This class asides from being a collection and providing us with the perfect base for our collections, it also manages viewstate automagically for us. In the past if your collection was inheriting CollectionBase, now you can instead inherit StateManagedCollection. But first why do we need a collection ? I mean we could always just expose a property of type ArrayList of objects ?

But then an ArrayList overgoes a lot of un-necessary boxing and unboxing, this is a useless overhead. But so what, asp.net 2.0 now supports generics and we can use the generic list ? The overhead is gone and we have a nice collection exposed, so why go through the trouble of creating a custom collection ? Especially when a generic List is giving us a nice easy, effortless collection Vs writing a strongly typed wrapper collection ourselves. extract from msdn docs :
It is to your advantage to use the type-specific implementation of the List class instead of using the ArrayList class or writing a strongly typed wrapper collection yourself. The reason is your implementation must do what the .NET Framework does for you already, and the common language runtime can share Microsoft intermediate language code and metadata, which your implementation cannot.
Now keeping this note in mind, consider that not always the List is what we want and we will need to resort to a custom collection we write ourselves. A good example is when we want the items in our list to participate in viewstate. I was unable to get this type of functionality.

The list does not implement IStateManager, so it does not manage saving viewstate by itself. In order to save the items in our list, in viewstate, when using a generic list, we are going to have to make sure that our items contained in the list do not in turn contain complex types. If they do, then we need to make sure that those complex types are decorated with the serialize() attribute.

That way their values can be serialized into viewstate. This is not usually in your control when you have a complex type like the Style class. Since this class has not been marked as serializable, you cannot just inherit it and mark your class as serializable and then voila. This will not work.
Serialization cannot be added to a class after it has been compiled. Also not to mention that using this approach of marking our class as serializable will result in pretty bad performance, especially in bloated data and overhead of serialization which takes quite a long time.

So for complex types like these, since we cant serialize them, we also cant have them participate in our controls viewstate. Remember complex types must manage their own viewstate by implementing IStateManager. If however the properties types that we are exposing in our Item are basic types and/or complex types that we have control over, this is possible.

However it looks pretty dirty imho. The reason I say dirty is becuase we can not control at the property level how items are adding to viewstate, meaning that we will be storing all items in viewstate whether their values have changed or not. This is not very convenient for me and results in a beautiful bloated viewstate which i prefer to avoid.

These advantages are however there when writing your own custom collections class and its not all that hard. Here in this post, i want to demonstrate how to create your own custom collection while allowing it to participate in viewstate for FREE*, that is with very little effort coming from your part by using a new class in the .net framework -->> StateManagedCollection.

In addition creating our custom collection also allows us to nest collections within collections, within collections, which i have found very useful. This, i am not demonstrating here but once you know how to create a custom collection, nesting collections is very simple and a no brainer so i dont want to waste time here with that.

I am not going to walk you through the code, because its very simple, basic code example. I have left comments inline, some useless, some are good notes and understanding them is essential, so make sure you dont miss them.

Note that since we will be using a CollectionEditor, so that you can get a beautiful dialog for adding items to your collection at designtime in visual studio 2005, then you need to reference one managed dll : System.Design. this dll is not added into a web application project by default, so you have to reference it yourself.

The output of this custom control are the 4 items we added to our collection, each item represented with a color. There is no purpose for this control other than to demonstrate collections, so functionality wise its pretty dumb. I tried to keep it as simple as possible. Posting back by clicking the button in the webform should cause the control to load these values from viewstate, and its perfect to see how this is all working nicely and here is the rendered output :

//MyControlItem.cs

using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace CustomControl
{
 /// <summary>
 /// Summary description for MyControlItem
 /// </summary>
 public class MyControlItem : IStateManager
 {
  public MyControlItem()
  {
  }
  public string MyProperty
  {
   get
   {
    object o = ViewState["MyPropertyValue"];
    return (o == null) ? string.Empty : o.ToString();
   }
   set { ViewState["MyPropertyValue"] = value; }
  }
  /*
   Only need to call TrackViewState on the complex type (style) itself. 
   The style class
   or rather the complex type is always responsible for maintaining its 
   own viewstate. Our job is to simply call LoadViewState, 
   SaveViewState and TrackViewState methods exposed by our complex types. 
   To see that, look at the LoadViewState, SaveViewState and 
   TrackViewState methods in this class. Since this is a 
    complex type too it implements IStateManager interface. Our Style
   class also implements IStateManager like we are doing here..
   */
  private Style myControlStyleValue = null;
  [DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
  PersistenceMode(PersistenceMode.InnerProperty), NotifyParentProperty(true)]
  public Style MyControlStyle
  {
   get
   {
    if (myControlStyleValue == null)
    {
     myControlStyleValue = new Style();
     if (isTrackingViewStateValue)
     {
      ((IStateManager)(myControlStyleValue)).TrackViewState();
     }
    }
    return myControlStyleValue;
   }
  }
  /*
   Store all our properties in the statebag exposed by this property. 
   All properties that are not
   complex types can be stored by our custom implementation of the statebag.
   */
  private StateBag viewStateValue;
  private StateBag ViewState
  {
   get
   {
    if (viewStateValue == null)
    {
     viewStateValue = new StateBag(false);
     if (isTrackingViewStateValue)
     {
      ((IStateManager)viewStateValue).TrackViewState();
     }
    }
    return viewStateValue;
   }
  }
  private bool isTrackingViewStateValue;
  bool IStateManager.IsTrackingViewState
  {
   get
   {
    return isTrackingViewStateValue;
   }
  }

  object IStateManager.SaveViewState()
  {
   return this.SaveViewState();
  }
  void IStateManager.LoadViewState(object savedState)
  {
   this.LoadViewState(savedState);
  }

  internal void LoadViewState(object savedState)
  {
   object[] states = (object[])savedState;
   if (states != null)
   {
    ((IStateManager)ViewState).LoadViewState(states[0]);
    ((IStateManager)MyControlStyle).LoadViewState(states[1]);
   }
  }
  internal object SaveViewState()
  {
   object[] states = new object[2];
   states[0] = (viewStateValue != null) ?
     ((IStateManager)viewStateValue).SaveViewState() : null;
   states[1] = (myControlStyleValue != null) ?
     ((IStateManager)myControlStyleValue).SaveViewState() : null;
   return states;
  }

  void IStateManager.TrackViewState()
  {
   isTrackingViewStateValue = true;
   if (viewStateValue != null)
    ((IStateManager)viewStateValue).TrackViewState();
   if (myControlStyleValue != null)
    ((IStateManager)myControlStyleValue).TrackViewState();
  }
  /*
   This is important. Without this call, state wont be maintained.
   This method is being called by our collections SetDirtyObject method, 
   which in turn is called internally by StateManagedCollection's 
   SaveViewState method, Add, and Insert methods.
   The SetDirtyObject allows you to save viewstate information 
   at the element level.
   In this method call of SetDirty we call set dirty individually
   only on the properties that manage state themselves.
   So its the only way that this class will know its 
   items have changed and it needs to save it to viewstate.
   Forget to do this and viewstate wont be maintained.
   */
  internal void SetDirty()
  {
   viewStateValue.SetDirty(true);
   if (myControlStyleValue != null)
    myControlStyleValue.SetDirty();
  }
 }
}

//MyControlCollection.cs

using System;
using System.Web.UI;
using System.Collections;
namespace CustomControl
{
 /// <summary>
 /// Summary description for MyControlCollection
 /// </summary>
 public class MyControlCollection : StateManagedCollection
 {
  public MyControlCollection()
  {
  }

  private static readonly Type[] knownTypes;
  static MyControlCollection()
  {
   MyControlCollection.knownTypes = new Type[] { typeof(MyControlItem) };
  }
  /*Note how we cast to IList, this is because 
   our base class implements IList
   and adds our elements to an IList.*/
  public MyControlItem this[int index]
  {
   get
   {
    return (MyControlItem)((IList)this)[index];
   }
   set
   {
    ((IList)this)[index] = value;
   }
  }
  // Add an item to the collection
  public int Add(MyControlItem value)
  {
   return ((IList)this).Add(value);
  }
  // Get the index of the value, passed in as parameter
  public int IndexOf(MyControlItem value)
  {
   return ((IList)this).IndexOf(value);
  }

  //Insert an item in the collection by index
  public void Insert(int index, MyControlItem value)
  {
   ((IList)this).Insert(index, value);
  }
  /*Copies the elements of the StateManagedCollection collection to an array,
    starting at a particular array index*/
  public void CopyTo(MyControlItem[] toolBarSetArray, int index)
  {
   base.CopyTo(toolBarSetArray, index);
  }
  /* Overide and create an instance of the class that implements 
     IStateManager.
     This class is ofcourse our item that we are adding to our collection. 
     In this case the class is MyControlItem. its used internally by 
     the base class (StateManagedCollection) when
     saving the item in viewstate. If by chance your class type can vary, 
     then you can check the index and create a new class based on 
     the index value. the index will vary based on
     the order of the types you specified in GetKnownTypes. 
     For a good concreate example look up
     StateManagedCollection class on msdn. 
     For this example, we have only one type, and the index
     is always zero, so there is no need to check the index before 
     returning a new instance of our type*/
  protected override object CreateKnownType(int index)
  {
   return new MyControlItem();
  }
  /*This method should return an array of IStateManager types that
    the StateManagedCollection collection can contain.
    For a good concreate example look up 
    StateManagedCollection class on msdn.*/
  protected override Type[] GetKnownTypes()
  {
   return MyControlCollection.knownTypes;
  }
  // Remove an item from the collection
  public void Remove(MyControlItem value)
  {
   ((IList)this).Remove(value);
  }
  //Remove an item by index from the collection
  public void RemoveAt(int index)
  {
   ((IList)this).RemoveAt(index);
  }
  //Does our collection contain the following item in the collection ?
  public bool Contains(MyControlItem value)
  {
   // If value is not of type MyControlItem, this will return false.
   return ((IList)this).Contains(value);
  }
  // Validate and allow types that your collection will contain
  protected override void OnValidate(Object value)
  {
   if (value.GetType() != typeof(MyControlItem))
    throw new ArgumentException(
     "value must be of type MyControlItem.", "value");
  }
  /*instructs an object contained by the collection
    to record its entire state to view state,
    rather than recording only change information*/
  protected override void SetDirtyObject(object o)
  {
   if (o is MyControlItem)
   {
    ((MyControlItem)o).SetDirty();
   }
  }
 }
}
//MyControl.cs

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace CustomControl
{
 /// <summary>
 /// Summary description for MyControl
 /// </summary>
 public class MyControl : WebControl
 {
  public MyControl()
   : base("div")
  {
   //
   // TODO: Add constructor logic here
   //
  }
  private bool isLoadedFromViewState = true;
  private MyControlCollection myControlCollectionValue = null;
  [Category("Behavior"), Description("A collection of ToolBarItem's "),
  DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
  Editor(typeof(System.ComponentModel.Design.CollectionEditor),
  typeof(System.Drawing.Design.UITypeEditor)),
  PersistenceMode(PersistenceMode.InnerProperty)]
  public MyControlCollection MyControlCollection
  {
   get
   {
    if (myControlCollectionValue == null)
     myControlCollectionValue = new MyControlCollection();
    if (IsTrackingViewState)
    {
     ((IStateManager)(myControlCollectionValue)).TrackViewState();
    }
    return myControlCollectionValue;
   }
  }

  protected override void LoadViewState(object state)
  {
   if (state != null)
   {
    object[] states = (object[])state;
    base.LoadViewState(states[0]);
    ((IStateManager)MyControlCollection).LoadViewState(states[1]);
   }
  }

  protected override object SaveViewState()
  {
   object[] states = new object[2];

   states[0] = base.SaveViewState();
   states[1] = (myControlCollectionValue != null) ?
    ((IStateManager)myControlCollectionValue).SaveViewState() : null;
   return states;
  }

  protected override void TrackViewState()
  {
   base.TrackViewState();
   if (myControlCollectionValue != null)
    ((IStateManager)myControlCollectionValue).TrackViewState();
  }

  protected override void OnLoad(EventArgs e)
  {
   base.OnLoad(e);
   /*
    just create some defaults and add them to our collection. 
    Since we are adding new items to our collection at runtime, 
    viewstate should kick in during postback
    and rebuild the values from there. So only set some defaults 
    if our collection is empty. Meaning
    this is the first time we are adding items to our collection :-)
    This is perfect to test if viewstate on my collection is working.
    */
   if (this.MyControlCollection.Count < 1)
   {
    isLoadedFromViewState = false;
    string[] defaults = new string[] { "blue", "green", "yellow", "beige" };
    foreach (string d in defaults)
    {
     MyControlItem mci = new MyControlItem();
     mci.MyProperty = d;
     mci.MyControlStyle.BackColor =
       System.Drawing.Color.FromName(d);
     this.MyControlCollection.Add(mci);
    }
   }
  }
  protected override void CreateChildControls()
  {
   base.CreateChildControls();
   if (this.MyControlCollection.Count > 0)
   {
    this.Controls.Add(new LiteralControl(
      string.Format(
     "<h1>This collection was loaded from viewstate ? {0}</h1>",
     isLoadedFromViewState.ToString())));
    foreach (MyControlItem mci in this.MyControlCollection)
    {
     Label l = new Label();
     l.Text = mci.MyProperty;
     l.ControlStyle.CopyFrom(mci.MyControlStyle);
     this.Controls.Add(l);
    }
   }
   Button b = new Button();
   b.Text = "Lets postback, c'mon click me, please please click me :P";
   this.Controls.Add(b);// dummy postback to check viewstate
  }
 }
}

<%@ Page Language="C#" %>
<%@ Register TagPrefix="ctl" Namespace="CustomControl" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
 <form id="form1" runat="server">
    <div>
       <ctl:MyControl ID="MyControl1" runat="server"></ctl:MyControl>
    </div>
 </form>
</body>
</html>

ImageButton Control nested in a GridView Control throws EventValidation error.

Ever try to include an ImageButton control in your gridview and then end up with the following beautiful error message :

Invalid postback or callback argument. Event validation is enabled using <pages enableEventValidation="true"/> in configuration or <%@ Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.

Thats right, its an EventValidation issue. This also occurs only with an ImageButton nested in a gridview. Moreover, you must be calling DataBind on the gridview if its a postback as well. Code speaks a thousand words, so lets look an example when this could occur :
protected void Page_Load(object sender, EventArgs e)
{
  //if (!IsPostBack)
  //{
   BindGrid();
  //}
 }

 private void BindGrid()
 {
  ArrayList al = new ArrayList();
  al.Add("a");
  al.Add("b");
  al.Add("c");
  GridView1.DataSource = al;
  GridView1.DataBind();
}

protected void ImageButton1_Click(object sender, EventArgs e)
{
}
</script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <asp:GridView ID="GridView1" runat="server"  OnRowDataBound="GridView1_RowDataBound"> <Columns> <asp:TemplateField> <ItemTemplate> <asp:ImageButton ID="ImageButton1"  OnCommand="ImageButton1_Click" runat="server" /> </ItemTemplate> </asp:TemplateField> </Columns> </asp:GridView> </div> </form> </body> </html>
As you can see from the following sample code, we are rebinding our grid even on a postback, this means when the user posts back via our ImageButton, the grid will get rebound again. When this happens the gridview will recreate the gridview Vs pulling values from viewstate. By calling DataBind again in the page_load method, the Gridview recreates the child controls, and by the time our page gets to the Postback event handling phase, RaisePostBackEvent is fired in our imagecontrol(since it implements the IPostBackEventHandler interface).

RaisePostBackEvent is what calls the ValidateEvent method, with two arguments. 1st argument is the UniqueID of our ImageButton and the second argument is a string argument. The issue here is with the UnqiueID argument. It has the id of the wrong control. How, why, I dont know exactly. I just know that by the time the ImageControl reaches the render phase, it has the correct uniqueID again. Since the gridview is a templated control, it prefixes the control with a unique namespace which happens to be the rowID of the current row which gets appeneded along with the gridview's id. Its this row id going wrong somehow only during RaisePostBackEvent. This happens only with the gridview too. I have tested with a repeater for eg and this worked correctly without issues. The 3-4 possible solutions I can think of are :

1. Put an IsPostBack check and only bind the gridview if its a postback. This means the gridview will be repopulating from viewstate which obviously does not have this wrong rowid issue. So if you remove the comments from our previous code, your set. But not always populating from viewstate might be an option.

2. You can try setting a unique id for the row generated by the ItemTemplate in our gridview. YOu can try this in either the RowCreated method or the RowDataBound method. In this manner we do not depend anymore on INamingContainer generating a uniqueID for the row, which in turn gets prefixed on our control since the row is now the direct NamingContainer and the problem is solved, eg :

protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
        e.Row.ID = e.Row.RowIndex.ToString();
} 


and a 3rd way to work around this problem is to rebind the gridview at a later stage, a stage after RaisePostBackEvent has finished executing. Since EventValidation is called during this stage, its safe to rebind after this call. Meaning for eg, in the OnCommand/onclick event of our ImageButton or in the RowCommand method of the gridview. In short, calling rebind on the grid after the Postback event handling phase will resolve your issues. Also note that this issue is there only with the ImageButton, the same does not happen if you used a LinkButton for eg, or a Button control.

And yet a 4th way to work around this is to use a LinkButton control and nest an image in it making it behave like an ImageButton.

So why does this happen only with the ImageButton control ? This is because the ImageButton implements IPostBackDataHandler interface. Image elements unlike other form elements include the x,y co-ordinates location in the image where you clicked. In order to detect this change and raise an event, the ImageButton control needs to provide implementation for LoadPostData method. Also this is the same place where a call is being made for RegisterRequiresRaiseEvent. RegisterRequiresRaiseEvent is also the cause of the issue here.

Without this call we have no problems with the uniqueID of the ImageControl, which is what made EventValidation to choke because the control was registered for EventValidation with one ID and then checked for Validation after validation with another ID. For me this unusual behaviour is a bug. We can make a simple test as the code that follows and the output of that code is not surprisingly the following and in this order :
  1. LoadPostData: GridView1:_ctl2:ImageButton1
  2. RaisePostBackEvent: GridView1:_ctl4:ImageButton1 
  3. AddAttributesToRender: GridView1:_ctl2:ImageButton1
Notice how in RaisePostBackEvent, the id of the control has changed magically and is now GridView1:_ctl4:ImageButton1. RaisePostBackEvent is also the method that registers the control for EventValidation. Notice that in a later phase AddAttributesToRender, the control id is back again to what it was, and this is also asif by magic! For me this is a bug. Also the cause is none other than the call being made to RegisterRequiresRaiseEvent from within LoadPostData, which you can test by following the inline comments i have included in the LoadPostData method.

Following is the code to verify this behaviour :

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls; 
/// <summary>
/// Summary description for CustomImageButton
/// </summary>
[SupportsEventValidation]
public class CustomImageButton : ImageButton
{
 public CustomImageButton()
 {
  //
  // TODO: Add constructor logic here
  //
  
 } 

 protected override bool LoadPostData(string postDataKey, 
 System.Collections.Specialized.NameValueCollection postCollection)
 {
  HttpContext.Current.Response.Write("<br/>LoadPostData: " + 
                         this.UniqueID);
  return base.LoadPostData(postDataKey, postCollection);
  // The reason this control chokes is because of 
  // the call being made to RegisterRequiresRaiseEvent.
  // To test this, first commentout the call to 
  // return base.LoadPostData(postDataKey, postCollection); 
                // above and then
  // remove the following comments below and and try it. 
  // It will work without issues!
         /*  if (this.Page != null)
  {
   this.Page.RegisterRequiresRaiseEvent(this);
  } 
  return false;*/
 }

 protected override void RaisePostBackEvent(string eventArgument)
 {
  HttpContext.Current.Response.Write("<br/>RaisePostBackEvent: " + 
                        this.UniqueID);
  // base.RaisePostBackEvent is what actually registers this 
                // control for EventValidation
  // so dont call it in order to test this unexpected 
                // behaviour, otherwise, event
  // validation will occur and you will get an error ofcourse :D
  // base.RaisePostBackEvent(eventArgument);
  
 }
 protected override void AddAttributesToRender(HtmlTextWriter writer)
 {
  HttpContext.Current.Response.Write("<br/>AddAttributesToRender: " + 
      this.UniqueID);
     base.AddAttributesToRender(writer);
 }
}

Sender is an object because of Event Guidelines!!

The .NET Framework uses delegates extensively for event-handling tasks like a button click event exposed by a button object. That said, lets look at why in asp.net, all the web controls that expose an event all expose the sender argument as type object. Why is this sender argument not strongly typed to the control that fired it ?

For eg. Drop a Button Web Control on your webform and subscribe to its click event. Why is the event skeleton for the event, created by your IDE, declaring the sender as object ?

So, the sender is of type object but when the event is fired, the sender holds a reference to the object that fired the event, couldnt we just strongly type it from the start. As we already know what type it is Vs doing a cast later in the method and evaluating at runtime. The simple answer is NO! Lets take our button control as an example.

The reason we cannot declare the sender as type button even though we already know its a button control that subscribed to it, is that, the delegate used to handle this event has this specific delegate signature. (object sender, EventArgs e) ; you cant change it.
As the name translates to, a delegate outsources* the task of calling a method to someone else. Delegates basically allow methods to be passed as parameters. In our button class example, the delegate used is of type EventHandler.

public delegate void EventHandler(Object sender, EventArgs e);

This delegate as you have guessed already can be used to call any method with a matching signature, that is a void return type and two parameters. 1 an object parameter(sender) and 2. an EventArgs parameter(e). So, nothing matters now but that the subscribers to this event must have a matching method with this exact signature, that is the argument list must match. There is actually an exception to this rule, and variance in the signature to a certain extent is allowed when matching delegate methods with delegate signatures. To slightly touch the subject, Covariance enables delegate methods to be created that can be used by both classes and derived classes.

So taking from our previous example, imagine having a delegate with a return type as :

public delegate Button EventHandler(Object sender, EventArgs e);

now our subscribers can subscribe to this delegate and the return type could be a more specific class, that is a class that derives from Button. Because the method's return type is more specific than the delegate signature's return type, it can be implicitly converted. The method is therefore acceptable for use as a delegate.

Contravariance on the other hand is When a delegate method signature has one or more parameters of types that are derived from the types of the method parameters, that method is said to be contravariant. As the delegate method signature parameters are more specific than the method parameters, they can be implicitly converted when passed to the handler method. so while Covariance allowed us to have a more specific return type for our delegates signature, Contravariance on the other hand allows us to have the same thing on the argument list.

So it is possible to define a different type for our sender argument, even though the delegate has the sender marked as object ?

Well, contravariance in this way did not work for us. It only works on concrete definitions of a class. Although they are sometimes used interchangeably, a class and an object are different things. A class defines a type of object, but it is not an object itself. An object is a concrete entity based on a class, and is sometimes called an instance of a class. So this is probably why we were unable to match signatures. If the delegate had the sender argument as type :

public delegate void CustomEventHandler(Button sender, EventArgs e);

Then contravariance would have worked in our previous example that used the EventHandler delegate if we had the argument of our method, for eg. being either a Button or a Class that derives Button Vs it being an object.

The only way to change it is within the class that declared the delegate, by changing the delegate arguments! So a good question now is, why do all the controls expose the sender as object ? Why couldnt they declare the delegates as a strongly typed class of the object in question. In our button example why is the button class using a delegate with the sender parameter as type object then.

This is what the documentation has to say in this regard :
The .NET Framework guidelines indicate that the delegate type used for an event should take two parameters: an object source parameter indicating the source of the event, and an event-specific parameter that encapsulates any additional information about the event. The event-specific parameter should derive from the EventArgs class. For events that do not use any additional information, the .NET Framework provides the EventHandler class.
So, you have it now. Its a guideline. It does not effect my life if the sender is an object type or strongly typed. I guess ultimately in the end its a matter of good design and a coding convention used by microsoft, which most of us will eventually adopt :-)

Another valid question you might want to ask yourselves is, why does the guide line suggest you to have a second parameter, an event-specific patameter that is of type EventArgs or dervies from EventArgs, even in the case when you dont have any EventArgs argument you want to pass. This has already been answered here :
http://blogs.msdn.com/kcwalina/archive/2005/11/18/WhyEventArgs.aspx

Image element with an empty string for the src="" attribute, results in making a call for Default.aspx

Ok, I dont know what you want to call this. A bug in IIS ? Or should we just say unexpected behaviour. Unexpected behaviour obviously, atleast for me since I were not prepared for and expecting this. It was especially very hard to track down and find out why everytime a page is requested, it would also execute a call to "default.aspx" on a seperate request, executing code in default.aspx for nothing. so i'm going to blog my findings here just in case someone else experiences this.

Basically when you have images on your pages, they are not going to be processed by the aspnet_isapi dll since image file extentions are not mapped to it by default.

First a small simple intro :
when the browser meets an <img element, it will try to make a request to the server. The request is going to be for the file in the src="" mce_src="" attribute of your <img element. However since image extentions are not mapped to the Aspnet_isapi.dll in IIS, it will completely bypass going through your application life cycle, since its the Aspnet_isapi.dll in turn which communicates the request to the worker process (aspnet_wp.exe).

Ok, now imagine if for whatever reason you have an image in your page, but the src resolved to an empty string -->> src="" seems a bit unlikely you might think, but what if. What do you think is going to happen ?

I am working on an opensource application and this is exactly what was happening. Basically there were image spacers being used. Image spacers are usually set to less than 10pixels in height and width so if the image were missing, it wont display that nice square box with an x in it, which made it almost impossible to notice.

I was doing some work in Application_AcquireRequestState event, to which i had subscribed to in a custom httpmodule. Everytime i would run this method in debug mode and i'd see it execute an extra time! So i look at the request to why and who is making this extra request and soon enough i see its a request for "default.aspx". But the page i ran is not default.aspx. This was basically driving me nuts. Why is default.aspx running, who is calling this, who made this request and the complexity of the application made it pretty hard to find out.

The best part was, even though default.aspx was being requested, it would render the page i had initially requested and render it correctly. I was freaking out! I just couldnt find a logical explaination for this and it took a while of eliminating pieces of code one by one and isolating every bit to eventually end up to the the image spacer line. I dont rem now if it were just a mere coincidence that i happened to suspect the image spacer element with the dynamic src, just luck maybe.

I dont remember.

But that was it. Basically someone had introduced a piece of code like this :
<img height="85" width="1" src="<%# Globals.GetSkinPath() + "/images/spacer.gif"%>">

This piece of code was supposed to pass a dynamic path for the skin path, which was
returning an empty string. The reason being that Page.DataBind was never called.

Changing that piece of code to simply the following resolved :
<img height="85" width="1" src="<%= Globals.GetSkinPath() + "/images/spacer.gif"%>">

note the abscence of the databinder symbol# ; code like this was all over the
application, which meant it would keep calling and re-executing default.aspx
everytime it found this image spacer that resulted with a src to an empty string.
Pretty expensive also because default.aspx was a pretty heavy page and almost difficult, if not impossible to notice. A couple of these spacers on each page and a call for default.aspx for each of these spacers and your application performance can be very very slow :-)

So lesson to learn, if you find extra requests being made and the requested file is for default.aspx, then you know this request is being made for a file on your webserver with an empty string for the path, which will result in looking for the default.aspx file in your current page folder. So what it will do is look for default.aspx.

so, why did src with an empty string resolves to Default.aspx. This is because IIS by default has "Enable default content page" set and the default page is as you maybe have guessed "Default.aspx" for an asp.net application. How beautiful, i spend a full evening debugging this :-)


Here is a simple test you can all try to test this behaviour :

This is page test.aspx :
-------------------------------------
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
string DynamicSrc = "emoticons.gif";
protected void Page_Load(object sender, EventArgs e)
{
// what if DynamicSrc resolved to string.Emtpy
DynamicSrc = string.Empty;
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>

</head>
<body>
<form id="form1" runat="server">
<div>
<img src="<%= DynamicSrc %>" style="height:5px;width:5px" />

</div>
</form>
</body>
</html>


code for the httpmodule, so we can test and verify unexpected behaviour :
--------------------------------------------------------------------------
using System;

using System.Web;
using System.Web.UI;

namespace Test
{
/// <summary>
/// Summary description for TesterHttpModule
/// </summary>

public class SimpleHttpModule : IHttpModule
{
public SimpleHttpModule()
{
//
// TODO: Add constructor logic here
//
}

/// <summary>
/// Initializes the HttpModule and performs the wireup of all application
/// events.
/// </summary>
/// <param name="application">Application the module is being run for</param>
public void Init(HttpApplication application)
{
// Wire-up application events
//
application.BeginRequest += 
                   new EventHandler(this.Application_BeginRequest);
application.AcquireRequestState += 
            new EventHandler(Application_AquireRequestState);
}

private void Application_BeginRequest(Object source, EventArgs e)
{
// do nothing. Just to see this event executing in debug mode
}
private void Application_AquireRequestState(object source, EventArgs e)
{
// Is this request made by the page
Page page1 = HttpContext.Current.Handler as Page;
string requestedUrl = HttpContext.Current.Request.Url.AbsolutePath;
System.Diagnostics.Debug.WriteLine("Request made for page: " + requestedUrl);
if (page1 != null)
{
// do nothing
}
}
}
}