【问题标题】:Dropdownlist control with <optgroup>s for asp.net (webforms)?带有 <optgroup> 的用于 asp.net (webforms) 的下拉列表控件?
【发布时间】:2020-02-16 06:07:21
【问题描述】:

任何人都可以推荐一个可以呈现选项组的asp.net(3.5)下拉列表控件吗?谢谢

【问题讨论】:

    标签: asp.net drop-down-menu custom-server-controls webforms


    【解决方案1】:

    我过去使用过标准控件,只是为它添加了一个简单的ControlAdapter,它将覆盖默认行为,以便它可以在某些地方呈现 。即使您有不需要特殊行为的控件,这也很有效,因为附加功能不会妨碍您。

    请注意,这是出于特定目的并使用 .Net 2.0 编写的,因此它可能不适合您,但至少应该为您提供一个起点。此外,您必须在项目中使用 .browserfile 将其连接起来(示例见文章末尾)。

    'This codes makes the dropdownlist control recognize items with "--"
    'for the label or items with an OptionGroup attribute and render them
    'as <optgroup> instead of <option>.
    Public Class DropDownListAdapter
        Inherits System.Web.UI.WebControls.Adapters.WebControlAdapter
    
        Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
            Dim list As DropDownList = Me.Control
            Dim currentOptionGroup As String
            Dim renderedOptionGroups As New Generic.List(Of String)
    
            For Each item As ListItem In list.Items
                Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value)
                If item.Attributes("OptionGroup") IsNot Nothing Then
                    'The item is part of an option group
                    currentOptionGroup = item.Attributes("OptionGroup")
                    If Not renderedOptionGroups.Contains(currentOptionGroup) Then
                        'the header was not written- do that first
                        'TODO: make this stack-based, so the same option group can be used more than once in longer select element (check the most-recent stack item instead of anything in the list)
                        If (renderedOptionGroups.Count > 0) Then
                            RenderOptionGroupEndTag(writer) 'need to close previous group
                        End If
                        RenderOptionGroupBeginTag(currentOptionGroup, writer)
                        renderedOptionGroups.Add(currentOptionGroup)
                    End If
                    RenderListItem(item, writer)
                ElseIf item.Text = "--" Then 'simple separator
                    RenderOptionGroupBeginTag("--", writer)
                    RenderOptionGroupEndTag(writer)
                Else
                    'default behavior: render the list item as normal
                    RenderListItem(item, writer)
                End If
            Next item
    
            If renderedOptionGroups.Count > 0 Then
                RenderOptionGroupEndTag(writer)
            End If
        End Sub
    
        Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter)
            writer.WriteBeginTag("optgroup")
            writer.WriteAttribute("label", name)
            writer.Write(HtmlTextWriter.TagRightChar)
            writer.WriteLine()
        End Sub
    
        Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter)
            writer.WriteEndTag("optgroup")
            writer.WriteLine()
        End Sub
    
        Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter)
            writer.WriteBeginTag("option")
            writer.WriteAttribute("value", item.Value, True)
            If item.Selected Then
                writer.WriteAttribute("selected", "selected", False)
            End If
    
            For Each key As String In item.Attributes.Keys
                writer.WriteAttribute(key, item.Attributes(key))
            Next key
    
            writer.Write(HtmlTextWriter.TagRightChar)
            HttpUtility.HtmlEncode(item.Text, writer)
            writer.WriteEndTag("option")
            writer.WriteLine()
        End Sub
    End Class
    

    这是同一类的 C# 实现:

    /* This codes makes the dropdownlist control recognize items with "--"
     * for the label or items with an OptionGroup attribute and render them
     * as <optgroup> instead of <option>.
     */
    public class DropDownListAdapter : WebControlAdapter
    {
        protected override void RenderContents(HtmlTextWriter writer)
        {
            //System.Web.HttpContext.Current.Response.Write("here");
            var list = (DropDownList)this.Control;
            string currentOptionGroup;
            var renderedOptionGroups = new List<string>();
    
            foreach (ListItem item in list.Items)
            {
                Page.ClientScript.RegisterForEventValidation(list.UniqueID, item.Value);
                //Is the item part of an option group?
                if (item.Attributes["OptionGroup"] != null)
                {
                    currentOptionGroup = item.Attributes["OptionGroup"];
                    //Was the option header already written, then just render the list item
                    if (renderedOptionGroups.Contains(currentOptionGroup))
                        RenderListItem(item, writer);
                    //The header was not written,do that first
                    else
                    {
                        //Close previous group
                        if (renderedOptionGroups.Count > 0)
                            RenderOptionGroupEndTag(writer);
    
                        RenderOptionGroupBeginTag(currentOptionGroup, writer);
                        renderedOptionGroups.Add(currentOptionGroup);
                        RenderListItem(item, writer);
                    }
                }
                //Simple separator
                else if (item.Text == "--")
                {
                    RenderOptionGroupBeginTag("--", writer);
                    RenderOptionGroupEndTag(writer);
                }
                //Default behavior, render the list item as normal
                else
                    RenderListItem(item, writer);
            }
    
            if (renderedOptionGroups.Count > 0)
                RenderOptionGroupEndTag(writer);
        }
    
        private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
        {
            writer.WriteBeginTag("optgroup");
            writer.WriteAttribute("label", name);
            writer.Write(HtmlTextWriter.TagRightChar);
            writer.WriteLine();
        }
        private void RenderOptionGroupEndTag(HtmlTextWriter writer)
        {
            writer.WriteEndTag("optgroup");
            writer.WriteLine();
        }
        private void RenderListItem(ListItem item, HtmlTextWriter writer)
        {
            writer.WriteBeginTag("option");
            writer.WriteAttribute("value", item.Value, true);
            if (item.Selected)
                writer.WriteAttribute("selected", "selected", false);
    
            foreach (string key in item.Attributes.Keys)
                writer.WriteAttribute(key, item.Attributes[key]);
    
            writer.Write(HtmlTextWriter.TagRightChar);
            HttpUtility.HtmlEncode(item.Text, writer);
            writer.WriteEndTag("option");
            writer.WriteLine();
        }
    }
    

    我的浏览器文件名为“App_Browsers\BrowserFile.browser”,如下所示:

    <!--
        You can find existing browser definitions at
        <windir>\Microsoft.NET\Framework\<ver>\CONFIG\Browsers
    -->
    <browsers>
       <browser refID="Default">
          <controlAdapters>
            <adapter controlType="System.Web.UI.WebControls.DropDownList" 
                   adapterType="DropDownListAdapter" />
          </controlAdapters>
       </browser>
    </browsers>
    

    【讨论】:

    • 这对于覆盖 Web 应用程序中的所有下拉列表非常有用。但是有没有类似的解决方案来创建一个只能在需要选项组的地方使用的自定义控件?
    • 您可以改为从下拉列表控件继承,并调整此代码以覆盖该控件的呈现事件。但实际上,这段代码不应该妨碍页面上的其他列表。
    • 我添加了一个可以修复视图状态错误的版本。
    • 我通过谷歌搜索找到了这个解决方案,并在几分钟内就可以在我现有的网络应用程序中实现它。辉煌;)
    • 我实现了这个,但是我在 any 页面上收到了一个事件验证错误,并且在此之后出现了一个下拉菜单。其他人有这个问题吗?
    【解决方案2】:

    Sharp Pieces project on CodePlex 解决了这个(以及其他几个)控制限制。

    【讨论】:

      【解决方案3】:

      谢谢乔尔!大家...如果需要,这里是 C# 版本:

      
      
      using System;
      using System.Web.UI.WebControls.Adapters;
      using System.Web.UI;
      using System.Web.UI.WebControls;
      using System.Collections.Generic;
      using System.Web;
      
      //This codes makes the dropdownlist control recognize items with "--"'
      //for the label or items with an OptionGroup attribute and render them'
      //as  instead of .'
      public class DropDownListAdapter : WebControlAdapter
      {
      
          protected override void RenderContents(HtmlTextWriter writer)
          {
              DropDownList list = Control as DropDownList;
              string currentOptionGroup;
              List renderedOptionGroups = new List();
      
              foreach(ListItem item in list.Items)
              {
                  if (item.Attributes["OptionGroup"] != null)
                  {
                      //'The item is part of an option group'
                      currentOptionGroup = item.Attributes["OptionGroup"];
                      //'the option header was already written, just render the list item'
                      if(renderedOptionGroups.Contains(currentOptionGroup))
                          RenderListItem(item, writer);
                      else
                      {
                          //the header was not written- do that first'
                          if (renderedOptionGroups.Count > 0)
                              RenderOptionGroupEndTag(writer); //'need to close previous group'
                          RenderOptionGroupBeginTag(currentOptionGroup, writer);
                          renderedOptionGroups.Add(currentOptionGroup);
                          RenderListItem(item, writer);
                      }
                  }
                  else if (item.Text == "--") //simple separator
                  {
                      RenderOptionGroupBeginTag("--", writer);
                      RenderOptionGroupEndTag(writer);
                  }
                  else
                  {
                      //default behavior: render the list item as normal'
                      RenderListItem(item, writer);
                  }
              }
      
              if(renderedOptionGroups.Count > 0)
                  RenderOptionGroupEndTag(writer);
          }
      
          private void RenderOptionGroupBeginTag(string name, HtmlTextWriter writer)
          {
              writer.WriteBeginTag("optgroup");
              writer.WriteAttribute("label", name);
              writer.Write(HtmlTextWriter.TagRightChar);
              writer.WriteLine();
          }
      
          private void RenderOptionGroupEndTag(HtmlTextWriter writer)
          {
              writer.WriteEndTag("optgroup");
              writer.WriteLine();
          }
      
          private void RenderListItem(ListItem item, HtmlTextWriter writer)
          {
              writer.WriteBeginTag("option");
              writer.WriteAttribute("value", item.Value, true);
              if (item.Selected)
                  writer.WriteAttribute("selected", "selected", false);
      
      
              foreach (string key in item.Attributes.Keys)
                  writer.WriteAttribute(key, item.Attributes[key]);
      
              writer.Write(HtmlTextWriter.TagRightChar);
              HttpUtility.HtmlEncode(item.Text, writer);
              writer.WriteEndTag("option");
              writer.WriteLine();
          }
      
      }
      
      
      
      

      【讨论】:

      • 有没有办法从sql结果中获取数据源,我尝试了以下方法,ddlCountry.Attributes.Add("OptionGroup", "Region");我需要将区域与区域绑定为 optgroup
      【解决方案4】:

      我用反射器看看为什么不支持。这是有原因的。在 ListControl 的渲染方法中,不存在创建 optgroup 的条件。

      protected internal override void RenderContents(HtmlTextWriter writer)
          {
              ListItemCollection items = this.Items;
              int count = items.Count;
              if (count > 0)
              {
                  bool flag = false;
                  for (int i = 0; i < count; i++)
                  {
                      ListItem item = items[i];
                      if (item.Enabled)
                      {
                          writer.WriteBeginTag("option");
                          if (item.Selected)
                          {
                              if (flag)
                              {
                                  this.VerifyMultiSelect();
                              }
                              flag = true;
                              writer.WriteAttribute("selected", "selected");
                          }
                          writer.WriteAttribute("value", item.Value, true);
                          if (item.HasAttributes)
                          {
                              item.Attributes.Render(writer);
                          }
                          if (this.Page != null)
                          {
                              this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                          }
                          writer.Write('>');
                          HttpUtility.HtmlEncode(item.Text, writer);
                          writer.WriteEndTag("option");
                          writer.WriteLine();
                      }
                  }
              }
          }
      

      所以我创建了自己的下拉控件,并覆盖了 RenderContents 方法。有我的控制。工作正常。我用的是和微软一模一样的代码,只是加了一点条件来支持 listItem 有属性 optgroup 来创建一个 optgroup 而不是一个选项。

      给我一​​些反馈

      public class DropDownListWithOptionGroup : DropDownList
        {
          public const string OptionGroupTag = "optgroup";
          private const string OptionTag = "option";
          protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
          {
            ListItemCollection items = this.Items;
            int count = items.Count;     
            string tag;
            string optgroupLabel;
            if (count > 0)
            {
              bool flag = false;
              for (int i = 0; i < count; i++)
              {
                tag = OptionTag;
                optgroupLabel = null;
                ListItem item = items[i];
                if (item.Enabled)
                {
                  if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null)
                  {
                    tag = OptionGroupTag;
                    optgroupLabel = item.Attributes[OptionGroupTag];
                  }           
                  writer.WriteBeginTag(tag);
                  // NOTE(cboivin): Is optionGroup
                  if (!string.IsNullOrEmpty(optgroupLabel))
                  {
                    writer.WriteAttribute("label", optgroupLabel);
                  }
                  else
                  {
                    if (item.Selected)
                    {
                      if (flag)
                      {
                        this.VerifyMultiSelect();
                      }
                      flag = true;
                      writer.WriteAttribute("selected", "selected");
                    }
                    writer.WriteAttribute("value", item.Value, true);
                    if (item.Attributes != null && item.Attributes.Count > 0)
                    {
                      item.Attributes.Render(writer);
                    }
                    if (this.Page != null)
                    {
                      this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                    }
                  }
                  writer.Write('>');
                  HttpUtility.HtmlEncode(item.Text, writer);
                  writer.WriteEndTag(tag);
                  writer.WriteLine();
                }
              }
            }
      
          }
        }
      

      【讨论】:

      • @cedric-boivin:我不得不稍微修改代码以使其正常工作。此外,如果您能提及 .aspx 页面代码的外观,那就更好了。如果您需要我,请告诉我。
      • aspx.cs中如何使用?
      【解决方案5】:

      正如上面所说的重载 RenderContents 方法的答案确实有效。您还必须记住更改视图状态。在 UpdatePanels 中使用未更改的视图状态时遇到了问题。这部分内容来自Sharp Pieces Project

       Protected Overloads Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
          Dim list As DropDownList = Me
      
          Dim currentOptionGroup As String
          Dim renderedOptionGroups As New List(Of String)()
      
          For Each item As ListItem In list.Items
              If item.Attributes("OptionGroup") Is Nothing Then
                  RenderListItem(item, writer)
              Else
                  currentOptionGroup = item.Attributes("OptionGroup")
      
                  If renderedOptionGroups.Contains(currentOptionGroup) Then
                      RenderListItem(item, writer)
                  Else
                      If renderedOptionGroups.Count > 0 Then
                          RenderOptionGroupEndTag(writer)
                      End If
      
                      RenderOptionGroupBeginTag(currentOptionGroup, writer)
                      renderedOptionGroups.Add(currentOptionGroup)
      
                      RenderListItem(item, writer)
                  End If
              End If
          Next
      
          If renderedOptionGroups.Count > 0 Then
              RenderOptionGroupEndTag(writer)
          End If
      End Sub
      
      Private Sub RenderOptionGroupBeginTag(ByVal name As String, ByVal writer As HtmlTextWriter)
          writer.WriteBeginTag("optgroup")
          writer.WriteAttribute("label", name)
          writer.Write(HtmlTextWriter.TagRightChar)
          writer.WriteLine()
      End Sub
      
      Private Sub RenderOptionGroupEndTag(ByVal writer As HtmlTextWriter)
          writer.WriteEndTag("optgroup")
          writer.WriteLine()
      End Sub
      
      Private Sub RenderListItem(ByVal item As ListItem, ByVal writer As HtmlTextWriter)
          writer.WriteBeginTag("option")
          writer.WriteAttribute("value", item.Value, True)
      
          If item.Selected Then
              writer.WriteAttribute("selected", "selected", False)
          End If
      
          For Each key As String In item.Attributes.Keys
              writer.WriteAttribute(key, item.Attributes(key))
          Next
      
          writer.Write(HtmlTextWriter.TagRightChar)
          HttpUtility.HtmlEncode(item.Text, writer)
          writer.WriteEndTag("option")
          writer.WriteLine()
      End Sub
      Protected Overrides Function SaveViewState() As Object
          ' Create an object array with one element for the CheckBoxList's
          ' ViewState contents, and one element for each ListItem in skmCheckBoxList
          Dim state(Me.Items.Count + 1 - 1) As Object 'stupid vb array
          Dim baseState As Object = MyBase.SaveViewState()
      
          state(0) = baseState
          ' Now, see if we even need to save the view state
          Dim itemHasAttributes As Boolean = False
      
          For i As Integer = 0 To Me.Items.Count - 1
              If Me.Items(i).Attributes.Count > 0 Then
                  itemHasAttributes = True
                  ' Create an array of the item's Attribute's keys and values
                  Dim attribKV(Me.Items(i).Attributes.Count * 2 - 1) As Object 'stupid vb array
                  Dim k As Integer = 0
                  For Each key As String In Me.Items(i).Attributes.Keys
                      attribKV(k) = key
                      k += 1
                      attribKV(k) = Me.Items(i).Attributes(key)
                      k += 1
                  Next
                  state(i + 1) = attribKV
              End If
          Next
          ' return either baseState or state, depending on whether or not
          ' any ListItems had attributes
          If (itemHasAttributes) Then
              Return state
          Else
              Return baseState
          End If
      End Function
      
      
      Protected Overrides Sub LoadViewState(ByVal savedState As Object)
          If savedState Is Nothing Then Return
          ' see if savedState is an object or object array
          If Not savedState.GetType.GetElementType() Is Nothing AndAlso savedState.GetType.GetElementType().Equals(GetType(Object)) Then
      
              ' we have just the base state
              MyBase.LoadViewState(savedState(0))
              'we have an array of items with attributes
              Dim state() As Object = savedState
              MyBase.LoadViewState(state(0))   '/ load the base state
              For i As Integer = 1 To state.Length - 1
                  If Not state(i) Is Nothing Then
                      ' Load back in the attributes
                      Dim attribKV() As Object = state(i)
                      For k As Integer = 0 To attribKV.Length - 1 Step +2
                          Me.Items(i - 1).Attributes.Add(attribKV(k).ToString(), attribKV(k + 1).ToString())
                      Next
                  End If
              Next
          Else
              'load it normal
              MyBase.LoadViewState(savedState)
          End If
      End Sub
      

      【讨论】:

        【解决方案6】:

        我已经使用 JQuery 来完成这项任务。我首先从后端为每个 ListItem 添加了一个新属性,然后在 JQuery wrapAll() 方法中使用该属性来创建组...

        C#:

        foreach (ListItem item in ((DropDownList)sender).Items)
        {
            if (System.Int32.Parse(item.Value) < 5)
                item.Attributes.Add("classification", "LessThanFive");
            else
                item.Attributes.Add("classification", "GreaterThanFive");
        } 
        

        JQuery:

        $(document).ready(function() {
            //Create groups for dropdown list
            $("select.listsmall option[@classification='LessThanFive']")
                .wrapAll("&lt;optgroup label='Less than five'&gt;");
            $("select.listsmall option[@classification='GreaterThanFive']")
                .wrapAll("&lt;optgroup label='Greater than five'&gt;"); 
        });
        

        【讨论】:

        【解决方案7】:

        根据上面的帖子,我创建了这个控件的 c# 版本,具有工作视图状态。

                public const string OptionGroupTag = "optgroup";
            private const string OptionTag = "option";
            protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
            {
                ListItemCollection items = this.Items;
                int count = items.Count;
                string tag;
                string optgroupLabel;
                if (count > 0)
                {
                    bool flag = false;
                    for (int i = 0; i < count; i++)
                    {
                        tag = OptionTag;
                        optgroupLabel = null;
                        ListItem item = items[i];
                        if (item.Enabled)
                        {
                            if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null)
                            {
                                tag = OptionGroupTag;
                                optgroupLabel = item.Attributes[OptionGroupTag];
                            }
                            writer.WriteBeginTag(tag);
                            // NOTE(cboivin): Is optionGroup
                            if (!string.IsNullOrEmpty(optgroupLabel))
                            {
                                writer.WriteAttribute("label", optgroupLabel);
                            }
                            else
                            {
                                if (item.Selected)
                                {
                                    if (flag)
                                    {
                                        this.VerifyMultiSelect();
                                    }
                                    flag = true;
                                    writer.WriteAttribute("selected", "selected");
                                }
                                writer.WriteAttribute("value", item.Value, true);
                                if (item.Attributes != null && item.Attributes.Count > 0)
                                {
                                    item.Attributes.Render(writer);
                                }
                                if (this.Page != null)
                                {
                                    this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                                }
                            }
                            writer.Write('>');
                            HttpUtility.HtmlEncode(item.Text, writer);
                            writer.WriteEndTag(tag);
                            writer.WriteLine();
                        }
                    }
                }
            }
        
                protected override object SaveViewState()
            {
                object[] state = new object[this.Items.Count + 1];
                object baseState = base.SaveViewState();
                state[0] = baseState;
                bool itemHasAttributes = false;
        
                for (int i = 0; i < this.Items.Count; i++)
                {
                    if (this.Items[i].Attributes.Count > 0)
                    {
                        itemHasAttributes = true;
                        object[] attributes = new object[this.Items[i].Attributes.Count * 2];
                        int k = 0;
        
                        foreach (string key in this.Items[i].Attributes.Keys)
                        {
                            attributes[k] = key;
                            k++;
                            attributes[k] = this.Items[i].Attributes[key];
                            k++;
                        }
                        state[i + 1] = attributes;
                    }
                }
        
                if (itemHasAttributes)
                    return state;
                return baseState;
            }
        
                protected override void LoadViewState(object savedState)
            {
                if (savedState == null)
                    return;
        
                if (!(savedState.GetType().GetElementType() == null) &&
                    (savedState.GetType().GetElementType().Equals(typeof(object))))
                {
                    object[] state = (object[])savedState;
                    base.LoadViewState(state[0]);
        
                    for (int i = 1; i < state.Length; i++)
                    {
                        if (state[i] != null)
                        {
                            object[] attributes = (object[])state[i];
                            for (int k = 0; k < attributes.Length; k += 2)
                            {
                                this.Items[i - 1].Attributes.Add
                                    (attributes[k].ToString(), attributes[k + 1].ToString());
                            }
                        }
                    }
                }
                else
                {
                    base.LoadViewState(savedState);
                }
            }
        

        我希望这可以帮助一些人:-)

        【讨论】:

          【解决方案8】:

          上面的代码在任何选项之前呈现 optgroup 的结束标记,因此除了标记不能正确表示分组之外,选项不会像它们应该的那样缩进。这是我对 Tom 的代码稍作修改的版本:

              public class ExtendedDropDownList : System.Web.UI.WebControls.DropDownList
          {
              public const string OptionGroupTag = "optgroup";
              private const string OptionTag = "option";
              protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)
              {
                  ListItemCollection items = this.Items;
                  int count = items.Count;
                  string tag;
                  string optgroupLabel;
                  if (count > 0)
                  {
                      bool flag = false;
                      string prevOptGroup = null;
                      for (int i = 0; i < count; i++)
                      {
                          tag = OptionTag;
                          optgroupLabel = null;
                          ListItem item = items[i];
                          if (item.Enabled)
                          {
                              if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes[OptionGroupTag] != null)
                              {
                                  optgroupLabel = item.Attributes[OptionGroupTag];
          
                                  if (prevOptGroup != optgroupLabel)
                                  {
                                      if (prevOptGroup != null)
                                      {
                                          writer.WriteEndTag(OptionGroupTag);
                                      }
                                      writer.WriteBeginTag(OptionGroupTag);
                                      if (!string.IsNullOrEmpty(optgroupLabel))
                                      {
                                          writer.WriteAttribute("label", optgroupLabel);
                                      }
                                      writer.Write('>');
                                  }
                                  item.Attributes.Remove(OptionGroupTag);
                                  prevOptGroup = optgroupLabel;
                              }
                              else
                              {
                                  if (prevOptGroup != null)
                                  {
                                      writer.WriteEndTag(OptionGroupTag);
                                  }
                                  prevOptGroup = null;
                              }
          
                              writer.WriteBeginTag(tag);
                              if (item.Selected)
                              {
                                  if (flag)
                                  {
                                      this.VerifyMultiSelect();
                                  }
                                  flag = true;
                                  writer.WriteAttribute("selected", "selected");
                              }
                              writer.WriteAttribute("value", item.Value, true);
                              if (item.Attributes != null && item.Attributes.Count > 0)
                              {
                                  item.Attributes.Render(writer);
                              }
                              if (optgroupLabel != null)
                              {
                                  item.Attributes.Add(OptionGroupTag, optgroupLabel);
                              }
                              if (this.Page != null)
                              {
                                  this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                              }
          
                              writer.Write('>');
                              HttpUtility.HtmlEncode(item.Text, writer);
                              writer.WriteEndTag(tag);
                              writer.WriteLine();
                              if (i == count - 1)
                              {
                                  if (prevOptGroup != null)
                                  {
                                      writer.WriteEndTag(OptionGroupTag);
                                  }
                              }
                          }
                      }
                  }
              }
          
              protected override object SaveViewState()
              {
                  object[] state = new object[this.Items.Count + 1];
                  object baseState = base.SaveViewState();
                  state[0] = baseState;
                  bool itemHasAttributes = false;
          
                  for (int i = 0; i < this.Items.Count; i++)
                  {
                      if (this.Items[i].Attributes.Count > 0)
                      {
                          itemHasAttributes = true;
                          object[] attributes = new object[this.Items[i].Attributes.Count * 2];
                          int k = 0;
          
                          foreach (string key in this.Items[i].Attributes.Keys)
                          {
                              attributes[k] = key;
                              k++;
                              attributes[k] = this.Items[i].Attributes[key];
                              k++;
                          }
                          state[i + 1] = attributes;
                      }
                  }
          
                  if (itemHasAttributes)
                      return state;
                  return baseState;
              }
          
              protected override void LoadViewState(object savedState)
              {
                  if (savedState == null)
                      return;
          
                  if (!(savedState.GetType().GetElementType() == null) &&
                      (savedState.GetType().GetElementType().Equals(typeof(object))))
                  {
                      object[] state = (object[])savedState;
                      base.LoadViewState(state[0]);
          
                      for (int i = 1; i < state.Length; i++)
                      {
                          if (state[i] != null)
                          {
                              object[] attributes = (object[])state[i];
                              for (int k = 0; k < attributes.Length; k += 2)
                              {
                                  this.Items[i - 1].Attributes.Add
                                      (attributes[k].ToString(), attributes[k + 1].ToString());
                              }
                          }
                      }
                  }
                  else
                  {
                      base.LoadViewState(savedState);
                  }
              }
          }
          

          像这样使用它:

                      ListItem item1 = new ListItem("option1");
                  item1.Attributes.Add("optgroup", "CatA");
                  ListItem item2 = new ListItem("option2");
                  item2.Attributes.Add("optgroup", "CatA");
                  ListItem item3 = new ListItem("option3");
                  item3.Attributes.Add("optgroup", "CatB");
                  ListItem item4 = new ListItem("option4");
                  item4.Attributes.Add("optgroup", "CatB");
                  ListItem item5 = new ListItem("NoOptGroup");
          
                  ddlTest.Items.Add(item1);
                  ddlTest.Items.Add(item2);
                  ddlTest.Items.Add(item3);
                  ddlTest.Items.Add(item4);
                  ddlTest.Items.Add(item5);
          

          这是生成的标记(为了便于查看而缩进):

           <select name="ddlTest" id="Select1">
              <optgroup label="CatA">
                  <option selected="selected" value="option1">option1</option>
                  <option value="option2">option2</option>
              </optgroup>
              <optgroup label="CatB">
                  <option value="option3">option3</option>
                  <option value="option4">option4</option>
              </optgroup>
              <option value="NoOptGroup">NoOptGroup</option>
           </select>
          

          【讨论】:

          • 注册类所在的命名空间跨度>
          【解决方案9】:
              // How to use:
              // 1. Create items in a select element or asp:DropDownList control
              // 2. Set value of an option or ListItem to "_group_", those will be converted to optgroups
              // 3. On page onload call createOptGroups(domElement), for example like this:
              //    - var lst = document.getElementById('lst');
              //    - createOptGroups(lst, "_group_");
              // 4. You can change groupMarkerValue to anything, I used "_group_"
              function createOptGroups(lst, groupMarkerValue) {
                  // Get an array containing the options
                  var childNodes = [];
                  for (var i = 0; i < lst.options.length; i++)
                      childNodes.push(lst.options[i]);
          
                  // Get the selected element so we can preserve selection
                  var selectedIndex = lst.selectedIndex;
                  var selectedChild = childNodes[selectedIndex];
                  var selectedValue = selectedChild.value;
          
                  // Remove all elements
                  while (lst.hasChildNodes())
                      lst.removeChild(lst.childNodes[0]);
          
                  // Go through the array of options and convert some into groups
                  var group = null;
                  for (var i = 0; i < childNodes.length; i++) {
                      var node = childNodes[i];
                      if (node.value == groupMarkerValue) {
                          group = document.createElement("optgroup");
                          group.label = node.text;
                          group.value = groupMarkerValue;
                          lst.appendChild(group);
                          continue;
                      }
          
                      // Add to group or directly under list
                      (group == null ? lst : group).appendChild(node);
                  }
          
                  // Preserve selected, no support for multi-selection here, sorry
                  selectedChild.selected = true;
              }
          

          在 Chrome 16、Firefox 9 和 IE8 上测试。

          【讨论】:

            【解决方案10】:

            Irfan's 基于 jQuery 的解决方案的更通用方法:

            后端

            private void _addSelectItem(DropDownList list, string title, string value, string group = null) {
               ListItem item = new ListItem(title, value);
               if (!String.IsNullOrEmpty(group))
               {
                   item.Attributes["data-category"] = group;
               }
               list.Items.Add(item);
            }
            
            ...
            _addSelectItem(dropDown, "Option 1", "1");
            _addSelectItem(dropDown, "Option 2", "2", "Category");
            _addSelectItem(dropDown, "Option 3", "3", "Category");
            ...
            

            客户

            var groups = {};
            $("select option[data-category]").each(function () {
                 groups[$.trim($(this).attr("data-category"))] = true;
            });
            $.each(groups, function (c) {
                 $("select option[data-category='"+c+"']").wrapAll('<optgroup label="' + c + '">');
            });
            

            【讨论】:

              【解决方案11】:

              我已经为 select 和 optgroups 使用了一个外部转发器,为该组中的项目使用了一个内部转发器:

              <asp:Repeater ID="outerRepeater" runat="server">
                  <HeaderTemplate>
                      <select id="<%= outerRepeater.ClientID %>">
                  </HeaderTemplate>
                  <ItemTemplate>
                          <optgroup label="<%# Eval("GroupText") %>">
                              <asp:Repeater runat="server" DataSource='<%# Eval("Items") %>'>
                                  <ItemTemplate>
                                      <option value="<%# Eval("Value") %>"><%# Eval("Text") %></option>
                                  </ItemTemplate>
                              </asp:Repeater>
                          </optgroup>
                  </ItemTemplate>
                  <FooterTemplate>
                      </select>
                  </FooterTemplate>
              </asp:Repeater>
              

              outerRepeater 的数据源是一个简单的分组如下:

              var data = (from o in thingsToDisplay
                          group oby GetAlphaGrouping(o.Name) into g
                          orderby g.Key
                          select new
                          {
                              Alpha = g.Key,
                              Items = g
                          });
              

              并获取 alpha 分组字符:

              private string GetAlphaGrouping(string value)
              {
                  string firstChar = value.Substring(0, 1).ToUpper();
                  int unused;
              
                  if (int.TryParse(firstChar, out unused))
                      return "#";
              
                  return firstChar.ToUpper();
              }
              

              这不是一个完美的解决方案,但它确实有效。 正确 的解决方案是不再使用 WebForms,而是使用 MVC。 :)

              【讨论】:

                【解决方案12】:

                我对@9​​87654321@ 进行了扩展,创建了一个可以对其进行数据绑定的 DropDownList 控件。结果如下(可能会有所改进):

                public class UcDropDownListWithOptGroup : DropDownList
                {
                    public const string OptionGroupTag = "optgroup";
                    private const string OptionTag = "option";
                
                    protected override void RenderContents(HtmlTextWriter writer)
                    {
                        ListItemCollection items = this.Items;
                        int count = items.Count;
                        string tag;
                        string optgroupLabel;
                        if (count > 0)
                        {
                            bool flag = false;
                            string prevOptGroup = null;
                            for (int i = 0; i < count; i++)
                            {
                                tag = OptionTag;
                                optgroupLabel = null;
                                ListItem item = items[i];
                                if (item.Enabled)
                                {
                                    if (item.Attributes != null && item.Attributes.Count > 0 && item.Attributes["data-optgroup"] != null)
                                    {
                                        optgroupLabel = item.Attributes["data-optgroup"];
                
                                        if (prevOptGroup != optgroupLabel)
                                        {
                                            if (prevOptGroup != null)
                                            {
                                                writer.WriteEndTag(OptionGroupTag);
                                            }
                                            writer.WriteBeginTag(OptionGroupTag);
                                            if (!string.IsNullOrEmpty(optgroupLabel))
                                            {
                                                writer.WriteAttribute("label", optgroupLabel);
                                            }
                                            writer.Write('>');
                                        }
                                        item.Attributes.Remove(OptionGroupTag);
                                        prevOptGroup = optgroupLabel;
                                    }
                                    else
                                    {
                                        if (prevOptGroup != null)
                                        {
                                            writer.WriteEndTag(OptionGroupTag);
                                        }
                                        prevOptGroup = null;
                                    }
                
                                    writer.WriteBeginTag(tag);
                                    if (item.Selected)
                                    {
                                        if (flag)
                                        {
                                            this.VerifyMultiSelect();
                                        }
                                        flag = true;
                                        writer.WriteAttribute("selected", "selected");
                                    }
                                    writer.WriteAttribute("value", item.Value, true);
                                    if (item.Attributes != null && item.Attributes.Count > 0)
                                    {
                                        item.Attributes.Render(writer);
                                    }
                                    if (optgroupLabel != null)
                                    {
                                        item.Attributes.Add(OptionGroupTag, optgroupLabel);
                                    }
                                    if (this.Page != null)
                                    {
                                        this.Page.ClientScript.RegisterForEventValidation(this.UniqueID, item.Value);
                                    }
                
                                    writer.Write('>');
                                    HttpUtility.HtmlEncode(item.Text, writer);
                                    writer.WriteEndTag(tag);
                                    writer.WriteLine();
                                    if (i == count - 1)
                                    {
                                        if (prevOptGroup != null)
                                        {
                                            writer.WriteEndTag(OptionGroupTag);
                                        }
                                    }
                                }
                            }
                        }
                    }
                
                    protected override object SaveViewState()
                    {
                        object[] state = new object[this.Items.Count + 1];
                        object baseState = base.SaveViewState();
                        state[0] = baseState;
                        bool itemHasAttributes = false;
                
                        for (int i = 0; i < this.Items.Count; i++)
                        {
                            if (this.Items[i].Attributes.Count > 0)
                            {
                                itemHasAttributes = true;
                                object[] attributes = new object[this.Items[i].Attributes.Count * 2];
                                int k = 0;
                
                                foreach (string key in this.Items[i].Attributes.Keys)
                                {
                                    attributes[k] = key;
                                    k++;
                                    attributes[k] = this.Items[i].Attributes[key];
                                    k++;
                                }
                                state[i + 1] = attributes;
                            }
                        }
                
                        if (itemHasAttributes)
                            return state;
                        return baseState;
                    }
                
                    protected override void LoadViewState(object savedState)
                    {
                        if (savedState == null)
                            return;
                
                        if (!(savedState.GetType().GetElementType() == null) &&
                            (savedState.GetType().GetElementType().Equals(typeof(object))))
                        {
                            object[] state = (object[])savedState;
                            base.LoadViewState(state[0]);
                
                            for (int i = 1; i < state.Length; i++)
                            {
                                if (state[i] != null)
                                {
                                    object[] attributes = (object[])state[i];
                                    for (int k = 0; k < attributes.Length; k += 2)
                                    {
                                        this.Items[i - 1].Attributes.Add
                                            (attributes[k].ToString(), attributes[k + 1].ToString());
                                    }
                                }
                            }
                        }
                        else
                        {
                            base.LoadViewState(savedState);
                        }
                    }
                
                    protected override void PerformDataBinding(IEnumerable dataSource)
                    {
                        base.PerformDataBinding(dataSource);
                
                        if (!string.IsNullOrWhiteSpace(DataOptGroupField) && OptGroupTitles != null)
                        {
                            var currentItems = Items;
                            var dataSourceItems = dataSource.Cast<object>().ToList();
                
                            var staticItemsCount = Items.Count - dataSourceItems.Count;
                
                            for (var i = staticItemsCount; i < Items.Count; i++)
                            {
                                var dataSourceItem = dataSourceItems[i - staticItemsCount];
                                var optGroupValue = DataBinder.GetPropertyValue(dataSourceItem, DataOptGroupField);
                                currentItems[i].Attributes.Add("data-optgroup", OptGroupTitles[optGroupValue]);
                            }
                        }
                    }
                
                    public Dictionary<object, string> OptGroupTitles { get; set; }
                    public string DataOptGroupField { get; set; }
                }
                

                注意事项:

                • 您可以设置DataOptGroupField 属性,类似于DataTextField 和DataValueField。它定义了在确定正确的 optgroup 时应考虑数据绑定项的哪个属性。
                • 您可以设置一个 OptGroupTitles 属性,该属性定义应将 DataOptGroupField 中定义的属性转换为哪个 optgroup 标签。

                例如,在您的页面或用户控件中使用以下代码:

                <MyControls:UcDropDownListWithOptGroup runat="server" DataSourceID="dsX" DataTextField="MyDataTextField" DataValueField="MyDataValueField" DataOptGroupField="IsActive" OptGroupTitles='<%# MyGroupTitles %>' />
                

                其中数据绑定项的“IsActive”属性是一个布尔值。

                在页面或用户控件的代码隐藏中,您定义以下属性:

                public Dictionary<object, string> MyGroupTitles
                {
                    get
                    {
                        return new Dictionary<object, string>
                        {
                            { true, "Active" },
                            { false, "Inactive" }
                        };
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 2010-11-26
                  • 1970-01-01
                  • 2018-02-07
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-06-19
                  • 2016-05-06
                  • 1970-01-01
                  相关资源
                  最近更新 更多