At work today I tried to do something I though would be simple, I needed to add a check box control to a repeater's item template and have it fire the ItemCommand event on the repeater.  The button and link button controls do this without a problem but for some reason Microsoft decided that the check box was going to be different.  Luckily we have great tools like Reflector to help in situations like this, if you dig into the Repeater's source code you will find out that the ItemCommand event gets fired by event bubbling.  When you add a button or link button to a repeaters template and click on it it will eventually run this code as part of it's post back.

 

   1: protected virtual void RaisePostBackEvent(string eventArgument)
   2: {
   3:     base.ValidateEvent(this.UniqueID, eventArgument);
   4:     if (this.CausesValidation)
   5:     {
   6:         this.Page.Validate(this.ValidationGroup);
   7:     }
   8:     this.OnClick(EventArgs.Empty);
   9:     this.OnCommand(new CommandEventArgs(this.CommandName, this.CommandArgument));
  10: }
  11:  

Notice how the last line in this code calls "this.OnCommand" and passes a CommandEventArgs instance with command name and command argument.  The button and link button have this extra code while the checkbox does not.  The code for this method looks like this:

   1: protected virtual void OnCommand(CommandEventArgs e)
   2: {
   3:     CommandEventHandler handler = (CommandEventHandler) base.Events[EventCommand];
   4:     if (handler != null)
   5:     {
   6:         handler(this, e);
   7:     }
   8:     base.RaiseBubbleEvent(this, e);
   9: }
  10:  
  11:  

All the magic that makes the button and link button work is right here.  Calling "base.RaiseBubbleEvent" and passing it the same CommandEventArgs from above allows the repeater control to catch this event later on in its OnBubbleEvent.  The on bubble event for the repeater looks like this:

   1: protected override bool OnBubbleEvent(object sender, EventArgs e)
   2: {
   3:     bool flag = false;
   4:     if (e is RepeaterCommandEventArgs)
   5:     {
   6:         this.OnItemCommand((RepeaterCommandEventArgs) e);
   7:         flag = true;
   8:     }
   9:     return flag;
  10: }
  11:  

The ItemCommand event is only fired here and only if the eventargs type is RepeaterCommandEventArgs.  Since the standard CheckBox control does not call the RaiseBubbleEvent like a button or link button it's checkchanged event will never make it here.  But there is a very simple solution to make it work this way, all you have to do is create a custom checkbox control that calls RaiseBubbleEvent during it's OnCheckChanged event.  Here is a an example, this CheckBoxRepeaterAware control is the same as a regular CheckBox except for the addition of two properties and an overridden OnCheckChanged event. 

   1: public class CheckBoxRepeaterAware : CheckBox
   2:     {
   3:         #region Properties
   4:  
   5:         #region CommandName
   6:  
   7:         public string CommandName
   8:         {
   9:             get
  10:             {
  11:                 if (this.ViewState["CommandName"] == null)
  12:                 {
  13:                     return string.Empty;
  14:                 }
  15:                 else
  16:                 {
  17:                     return this.ViewState["CommandName"] as string;
  18:                 }
  19:             }
  20:             set
  21:             {
  22:                 this.ViewState["CommandName"] = value;
  23:             }
  24:         }
  25:  
  26:         #endregion
  27:  
  28:         #region CommandArgument
  29:  
  30:         public string CommandArgument
  31:         {
  32:             get
  33:             {
  34:                 if (this.ViewState["CommandArgument"] == null)
  35:                 {
  36:                     return string.Empty;
  37:                 }
  38:                 else
  39:                 {
  40:                     return this.ViewState["CommandArgument"] as string;
  41:                 }
  42:             }
  43:             set
  44:             {
  45:                 this.ViewState["CommandArgument"] = value;
  46:             }
  47:         }
  48:  
  49:         #endregion
  50:  
  51:         #endregion
  52:  
  53:         #region Procedures
  54:  
  55:         #region On Checked Changed
  56:  
  57:         protected override void OnCheckedChanged(EventArgs e)
  58:         {
  59:             //create a new event args of type command event args
  60:             CommandEventArgs ce = new CommandEventArgs(this.CommandName, this.CommandArgument);
  61:  
  62:             //allow the base checkbox to handle the event as normal
  63:             base.OnCheckedChanged(e);
  64:  
  65:             //raise the contorls method RaiseBubbleEvent
  66:             base.RaiseBubbleEvent(this, ce);
  67:  
  68:         }
  69:         #endregion
  70:  
  71:  
  72:         #endregion
  73:  
  74:     }

 

As you can tell, in the OnCheckChanged event we are calling "base.RaiseBubbleEvent" and passing it the command name and command argument.  By doing this, the repeater will be able to fire it's ItemCommand event for us.  Again, I am not sure why Microsoft didn't just implement this in the first place for the check box control but at least the framework is flexible enough that we can do it now.  The last thing we need to do to make this solution even easier is add a tag mapping in our web config to allow the new CheckBoxRepeaterAware control to replace all instances of CheckBox controls in our application.  To do that we add the following to the web config inside the pages node:

   1: <tagMapping>
   2:     <add tagType="System.Web.UI.WebControls.CheckBox" mappedTagType="CheckBoxRepeaterAwareDemo.Classes.CheckBoxRepeaterAware"/>
   3: </tagMapping>

 

I made a sample project where you can see it working in action, download the sample code here.