Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Rich List Controls in ASP.NET

0.00/5 (No votes)
1 Nov 2005 2  
Improvements to the standard ASP.NET list controls including styles for individual list items and an improved API.

Introduction

This article chronicles my quest to both improve upon the existing ASP.NET list controls but also to learn about some of the hidden mysteries of Custom Web Controls.

Background

Custom Web Controls have been one of my favorite features of the ASP.NET framework from the time I first started using it three years ago. I had been doing web development for many years before that and the idea of being able to encapsulate my logic into controls was very appealing. The largest problem I kept running into when developing my own controls was the poor documentation on some of the advanced features. I looked at the simple controls I was developing, then I looked at rich controls such as the DataGrid, and I noticed a large disparity. I found many articles on custom web controls and even read this book. Although these resources were helpful, they did not reveal the hidden secrets of the Microsoft web controls.

This article explains some of the things I learned while developing my own version of the ASP.NET list controls. I found the individual list items (RadioButton and CheckBox) to be very rich but when placed inside of their respective list controls (RadioButtonList and CheckBoxList) they list a lot of functionality. I wanted to be able to customize the styles for the individual list items and to have tooltips as well. I did, however, really like some of the features that the list controls afforded me such as data binding and layout control. When I discovered Lutz Roeder's Reflector, I decided to pick apart the Microsoft controls in an effort to write my own.

Notable Findings

Some of the code used in the ASP.NET list controls is marked as internal so us lowly non-Microsoft developers do not have access to them. To do this, I simply copied their code and made it public. There were some parts which were public but I had never seen documented. One such class is the interface System.Web.UI.WebControls.IRepeatInfoUser. This class facilitates the layout control for any repeating list of items. The most notable part of the interface is:

public void RenderItem(ListItemType itemType, int repeatIndex, 
  RepeatInfo repeatInfo, HtmlTextWriter writer);
This code is called for each item in the list so that you can render each item yourself. Here is the complete code for implementing this method in my RadioList Control:
public void RenderItem(ListItemType itemType, int repeatIndex, 
  RepeatInfo repeatInfo, HtmlTextWriter writer)
{
  RadioButton button1 = new RadioButton();
  button1.Page = Page;
  button1.GroupName = UniqueID;
  button1.ID = string.Concat(ClientID, "_", 
    repeatIndex.ToString(NumberFormatInfo.InvariantInfo));
  button1.Text = Items[repeatIndex].Text;
  button1.ToolTip = Items[repeatIndex].ToolTip;
  button1.Attributes["value"] = Items[repeatIndex].Value;
  button1.Checked = Items[repeatIndex].Selected;
  button1.TextAlign = TextAlign;
  button1.AutoPostBack = AutoPostBack;
  button1.TabIndex = radioButtonTabIndex;
  button1.Enabled = Enabled;

  foreach(string key in Items[repeatIndex].Attributes.Keys)
  {
    button1.Attributes[key] = Items[repeatIndex].Attributes[key];
  }

  button1.RenderControl(writer);
}
      
This code basically just using the information from the Items collection to build a RadioButton control and then to render it. It is that simple!

RichListItems

Most of the changes that I made were to the System.Web.UI.WebControls.ListItem class. I made my own version called RichListItem. I added a property of type System.Web.UI.WebControls.TableItemStyle called Style. This allows for each list item to have its own style settings. The attributes on this property are important when it comes to the behavior of this class being persisted when using the controls in design mode.

[
  Category("Behavior"),
  Description("The Style of this item"),
  DesignerSerializationVisibility(DesignerSerializationVisibility.Content),       
  NotifyParentProperty(true),
  PersistenceMode(PersistenceMode.Attribute)
]
      
By using PersistenceMode.Attribute, the style information for each list item is persisted as attributes (using the '-' naming convention). The DesignerSerializationVisibility.Content is what tells Visual Studio to persist the styles as content.

Besides adding styles to the list items, I added the ToolTip property. In order to do this, I had to change the ViewState persistence to save the ToolTip along with the Text and the Value of the list item.

internal object SaveViewState()
{
  if(misc.Get(TEXTISDIRTY) && misc.Get(VALUEISDIRTY) && misc.Get(TOOLTIPISDIRTY))
  {
    return new Triplet(Text, Value, ToolTip);
  }
  else if (misc.Get(TEXTISDIRTY) && misc.Get(VALUEISDIRTY))
  {
    return new Pair(Text, Value);
  }
  else if (misc.Get(TEXTISDIRTY))
  {
    return Text;
  }
  else if (misc.Get(VALUEISDIRTY))
  {
    return new Pair(null, Value);
  }
  else if(misc.Get(TOOLTIPISDIRTY))
  {
    return new Pair(ToolTip, null);
  }
  return null;
}

internal void LoadViewState(object state)
{
  Pair pair;
  Triplet triplet;
  if (state == null)
  {
    return;
  }
  if ((state as Triplet) != null)
  {
    triplet = ((Triplet) state);
    Text = (string)triplet.First;
    Value = (string)triplet.Second;
    ToolTip = (string)triplet.Third;
  }
  else if ((state as Pair) != null)
  {
    pair = ((Pair) state);
    if (pair.First != null)
    {
      if(pair.Second != null)
        Text = (string)pair.First;
      else
        ToolTip = (string) pair.First;
    }
    if(pair.Second != null)
    {
      Value = ((string) pair.Second);
    }
    return;
  }
  this.Text = ((string)state);
}
    
An in depth explanation of state management is beyond the scope of this article but note how the LoadViewState and the SaveViewState methods compliment each other. In the case of the RichListItem, there are three possible values to be persisted: Text, Value, and ToolTip.

API Changes

There are some breaking changes as far as making the RichListControls backwards compatible with the ASP.NET list controls. There is still a base class, RichListControl, which provides the basis for all list controls. I have added another level of inheritance in between the list controls and the base RichListControl class. I broke them out into two different types: SingleSelectListControl and MutipleSelectListControl. This allows list controls which allow only one item to be selected to have properties for SelectedIndex, SelectedItem, SelectedValue, and SelectedText. List controls which allow multiple items to be selected have properties for SelectedIndices, SelectedItems, SelectedValues, and SelectedTexts. I have added a property to the base RichListControl called HasSelection which indicates whether or not any items are selected in the list. Here is an example of a possible use of these properties:

if(list.HasSelection)
{
  foreach(RichListItem item in list.SelectedItems)
  {
    lblSelection.Text += String.Format(" {0}", item.Text);
  }
}
else
{
  lblSelection.Text = "No Selection";
}
      

Using the code

The RichListControls are used in the exact same way as the traditional ASP.NET list controls. Note the Style and ToolTip attributes below.

<%@ Register TagPrefix="List" Assembly="RichListControls" 
  Namespace="RichListControls" %>
<List:RichRadioButtonList id="list" runat="server">
  <Items>
    <list:RichListItem ToolTip="This is not that many miles" 
        Style-Font-Italic="True" Style-ForeColor="Green"
      Value="0">0 - 15</list:RichListItem>
    <list:RichListItem ToolTip="Most People Drive at least this much" 
          Value="5">15 - 30</list:RichListItem>
    <list:RichListItem Style-Font-Bold="True" Style-ForeColor="Firebrick" 
         Style-BackColor="LightYellow"
      Value="10">30+</list:RichListItem>
  </Items>
</List:RichRadioButtonList>
    

Points of Interest

There are a couple of helper classes I used which are worth noting. First, there is StyleHelper which is used by the RichDropDownList to convert a System.Web.UI.WebControls.Style to a string that can be used in an HTML style attribute. Most of this code is taken straight from internal methods in the System.Web dll.

There is also DataSourceHelper which is used in databinding. All of the code in this is taken from the System.Web dll.

History

  • 9/9/2004 - Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here