Now that we’ve written a bunch of code to show how anybody
can do this to a stock DropDownList control, let’s roll all of this code into a
single reusable control, the EnumDropDownList. One additional requirement
we’ll add to this control is that it optionally include a ListItem that simple
displays “All” and has no associated enum value. We’ll use this when the
DropDownList is being used to display filters on search results or reports, and
in this case the “All” value will mean that we don’t want to filter by this
enum. Similarly, to make our filtering easier, we’ll also include a nullable
integer property, SelectedIntegerValue, since the data access layer is going to
expect integer values for the statuses we choose to filter on (or null if
none). The control will include the GetEnumDataSource<T> method shown
earlier, and of course this could be refactored out into a separate utility
class where it would make more sense. I’ve included it in EnumDropDownList to
make the sample easier to follow. The complete source for EnumDropDownList is
shown in Listing 6.
Listing 6: EnumDropDownSource
public class EnumDropDownList<T> : DropDownList where T : struct
{
private bool _showAllOption = true;
public bool ShowAllOption
{
get
{
return _showAllOption;
}
set
{
_showAllOption = value;
}
}
public new T? SelectedValue
{
get
{
if (String.IsNullOrEmpty(this.SelectedItem.Text))
{
return null;
}
return (T)Enum.Parse(typeof(T), this.SelectedItem.Text);
}
}
public int? SelectedIntegerValue
{
get
{
if (ShowAllOption && this.SelectedIndex == 0)
{
return null;
}
if (String.IsNullOrEmpty(this.SelectedItem.Text))
{
return null;
}
return (int)Enum.Parse(typeof(T), this.SelectedValue.ToString());
}
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
if (typeof(T).BaseType != typeof(Enum))
{
throw new ArgumentException("Type T must inherit from System.Enum.");
}
BindData();
}
protected void BindData()
{
this.DataSource = GetEnumDataSource<T>();
this.DataTextField = "Key";
this.DataValueField = "Value";
this.DataBind();
if (ShowAllOption)
{
this.Items.Insert(0, new ListItem("All", ""));
}
}
public static SortedList<string, int> GetEnumDataSource<T>() where T : new()
{
Type myEnumType = typeof(T);
if (myEnumType.BaseType != typeof(Enum))
{
throw new ArgumentException("Type T must inherit from System.Enum.");
}
SortedList<string, int> returnCollection = new SortedList<string, int>();
string[] enumNames = Enum.GetNames(myEnumType);
for (int i = 0; i < enumNames.Length; i++)
{
returnCollection.Add(enumNames[i],
(int)Enum.Parse(myEnumType, enumNames[i]));
}
return returnCollection;
}
}
Now to use this control, unfortunately you can’t simply add
markup to the ASPX page. Some options
were discussed by Mikhail at Microsoft but I can’t find that anything was
ever implemented up through ASP.NET 3.5. Thus, you can only instantiate this
control from your C# or VB code, and then add it to the page using a
Placeholder control or similar technique. This is fairly minor, however, and
the resulting code in the page’s codebehind is shown in Listing 7.
PlaceHolder1 is defined on the page where we want the DropDownList to be shown.
Listing 7: Rendering the Control in a PlaceHolder
protected EnumDropDownList<ProposalStatus> ProposalStatusDropDownList1;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
ProposalStatusDropDownList1 = new EnumDropDownList<ProposalStatus>();
this.PlaceHolder1.Controls.Add(ProposalStatusDropDownList1);
}