# VBForums CodeBank > CodeBank - ASP / ASP.NET / MVC / Web API >  ASP.NET MVC ListBoxFor() with optgroup Tag support

## KGComputers

Hello,

By default, ASP.NET MVC ListBoxFor() helper does not have support for optgroup tag. To achieve this functionality, we either use jQuery or create a custom html helper. The example below demonstrates using the latter and is a VB.NET version of what we have developed in C#.

First, we define our ViewModel class for the countries with states and selected states.

VB.NET Code:
Public Class CountriesStatesViewModel    Public Property SelectedStates() As IEnumerable(Of String)    Public Property CountriesAndStates() As Dictionary(Of String, IEnumerable(Of SelectListItem))End Class

Next, we create the custom ListBoxFor() helper that supports optgroup tag.

VB.NET Code:
Public Module HtmlExtensions     <Extension()>    Public Function ListBoxFor(Of TModel, TProperty)(htmlHelper As HtmlHelper(Of TModel), expression As Expression(Of Func(Of TModel, TProperty)), _        selectList As Dictionary(Of String, IEnumerable(Of SelectListItem)), Optional htmlAttributes As Object = Nothing) As IHtmlString Dim selectTag = New TagBuilder("select")        selectTag.Attributes.Add("name", ExpressionHelper.GetExpressionText(expression))         If htmlAttributes IsNot Nothing Then            Dim routeValues As New RouteValueDictionary(htmlAttributes)            If Not routeValues.ContainsKey(("size").ToLower()) Then                selectTag.Attributes.Add("size", selectList.Sum(Function(x) x.Value.Count()).ToString())            End If             For Each item In routeValues                selectTag.Attributes.Add(item.Key, item.Value.ToString())            Next        Else            selectTag.Attributes.Add("size", selectList.Sum(Function(x) x.Value.Count()).ToString())        End If         Dim optgroups = New StringBuilder()         For Each kvp In selectList            Dim optgroup = New TagBuilder("optgroup")            optgroup.Attributes.Add("label", kvp.Key)             Dim options = New StringBuilder()             For Each item In kvp.Value                Dim optionTag = New TagBuilder("option")                 optionTag.Attributes.Add("value", item.Value)                optionTag.SetInnerText(item.Text) If item.Selected Then                    optionTag.Attributes.Add("selected", "selected")                End If                 options.Append(optionTag.ToString(TagRenderMode.Normal))            Next             optgroup.InnerHtml = options.ToString()             optgroups.Append(optgroup.ToString(TagRenderMode.Normal))        Next         selectTag.InnerHtml = optgroups.ToString()         Return MvcHtmlString.Create(selectTag.ToString(TagRenderMode.Normal))      End FunctionEnd Module

In our controller, we add two action result methods. The *Index action* adds data to our model and then pass it to our view for rendering. The *SaveEntry action* retrieves the selected states chosen by the user.


VB.NET Code:
Function Index() As ActionResult    Dim model As New CountriesStatesViewModel    Dim items As New Dictionary(Of String, IEnumerable(Of SelectListItem))    model.CountriesAndStates = New Dictionary(Of String, IEnumerable(Of SelectListItem))      items.Add("US", New List(Of SelectListItem)() From { _        New SelectListItem() With {.Text = "Arizona", .Value = "001", .Selected = False},        New SelectListItem() With {.Text = "Montana", .Value = "002", .Selected = False}    })      items.Add("AU", New List(Of SelectListItem)() From { _        New SelectListItem() With {.Text = "Queensland", .Value = "003", .Selected = False},        New SelectListItem() With {.Text = "Victoria", .Value = "004", .Selected = False}    })      items.Add("BR", New List(Of SelectListItem)() From { _        New SelectListItem() With {.Text = "Bahia", .Value = "005", .Selected = False},        New SelectListItem() With {.Text = "Minas Gerais", .Value = "006", .Selected = False}    })      model.CountriesAndStates = items      Return View(model)End Function  <HttpPost>Function SaveEntry(SelectedStates As IEnumerable(Of String)) As ActionResult    'get selected states    If SelectedStates IsNot Nothing Then        If SelectedStates.Count() > 0 Then            TempData("list") = SelectedStates.ToList() 'replace with your own code            Return RedirectToAction("SelectionSuccess") 'replace with your own code        End If    End If      Return RedirectToAction("Index") 'replace with your own codeEnd Function

And in our view (Index.vbhtml), make sure to reference the ViewModel class and Helper.

HTML Code:
@ModelType ASPMVCListBoxForHelper.CountriesStatesViewModel@Imports ASPMVCListBoxForHelper.Helpers<div>    @Using Html.BeginForm("SaveEntry", "Home", FormMethod.Post)        @Html.ListBoxFor(Function(t) t.SelectedStates, Model.CountriesAndStates, New With {.Multiple = "Multiple", .Size = Model.CountriesAndStates.Sum(Function(x) x.Value.Count())})        @<br />        @<input name="Save" type="submit" value="Save" />    End Using</div>

Screenshot


Source code: VB.NET Version|C# Version

That's it.. :-)

----------

