原生 <select> 支援 <optgroup> 分組,但 DropDownList 無對應屬性。繼承 DropDownList 重寫 RenderContents,依 ListItem.Value 特殊值識別分組標題,輸出標準 <optgroup>。
[ToolboxData("<{0}:SmartDropDownList runat=server>{0}:SmartDropDownList>")]
public class SmartDropDownList : DropDownList {
[Browsable(true), Category("擴展"), Description("分組項目 Value 值")]
public virtual string OptionGroupValue {
get => (string)ViewState["OptionGroupValue"] ?? "optgroup";
set => ViewState["OptionGroupValue"] = value;
}
protected override void RenderContents(HtmlTextWriter writer) {
bool groupOpened = false;
foreach (ListItem li in Items) {
if (li.Value != OptionGroupValue) {
RenderListItem(li, writer); // 普通 option
} else {
if (groupOpened) writer.WriteEndTag("optgroup");
writer.WriteBeginTag("optgroup");
writer.WriteAttribute("label", li.Text);
foreach (string key in li.Attributes.Keys)
writer.WriteAttribute(key, li.Attributes[key]);
writer.Write(HtmlTextWriter.TagRightChar); writer.WriteLine();
groupOpened = true;
}
}
if (groupOpened) writer.WriteEndTag("optgroup");
}
private void RenderListItem(ListItem li, HtmlTextWriter writer) {
writer.WriteBeginTag("option");
writer.WriteAttribute("value", li.Value, true);
if (li.Selected) writer.WriteAttribute("selected", "selected", false);
foreach (string key in li.Attributes.Keys)
writer.WriteAttribute(key, li.Attributes[key]);
writer.Write(HtmlTextWriter.TagRightChar);
HttpUtility.HtmlEncode(li.Text, writer);
writer.WriteEndTag("option"); writer.WriteLine();
}
}
<%@ Register Assembly="Controls" Namespace="mydream.Controls" TagPrefix="cc1" %>
<cc1:SmartDropDownList ID="SmartDropDownList1" runat="server" OptionGroupValue="optgroup" />
private void Bind(SmartDropDownList ddl) {
ddl.Items.Clear();
foreach (var group in GetGroups()) { // 取得大分類
ddl.Items.Add(new ListItem("--" + group.Name, "optgroup")); // 分組標題
foreach (var sub in GetSubItems(group.Id)) { // 子分類
ddl.Items.Add(new ListItem(sub.Name, sub.Id));
}
}
}
<select id="SmartDropDownList1">
<optgroup label="--群組一">
<option value="1">子項一</option>
<option value="2">子項二</option>
</optgroup>
<optgroup label="--群組二">
<option value="3">子項三</option>
</optgroup>
</select>
<optgroup> 原生行為),符合 UX 直覺。OptionGroupValue 可自訂識別值,避免與真實資料衝突。