How to bind an Html.DropDownList without ViewData (Strongly-Typed View)

Toran Billups picture Toran Billups · May 21, 2009 · Viewed 16.2k times · Source

I can't seem to find a good blog post that shows how to bind a model to a view without the magic strings "ViewData" (using a strongly-typed view is the approach I'm trying to take)

Does anyone know what I need to alter in the below to bind this directly to my model?

View

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of IEnumerable (Of MvcApplication1.Category))" %>
<asp:Content ID="aboutContent" ContentPlaceHolderID="MainContent" runat="server">
    <%=Html.DropDownList("CategoryList")%>
</asp:Content>

Controller

Function Index() As ActionResult
    Dim list As New List(Of Category)
    list.Add(New Category With {.CategoryID = 1, .CategoryName = "Test1"})
    list.Add(New Category With {.CategoryID = 2, .CategoryName = "Test2"})

    Return View()
End Function

EDIT

The final solution in VB is shown below, thanks for the big response!

Controller

Function Index() As ActionResult
    Dim id As Integer = 1
    Dim ProductObject As Product = mProductService.GetProductById(id)

    Return View(ProductObject)
End Function

View

<%=Html.DropDownList("Category", New SelectList(Model.Categories, "CategoryID", "CategoryName"))%>

Product Class (w/ a IEnumeralbe property for the categories)

Public Class Product
    Public Sub New()

    End Sub


    Private mProductID As Integer
    Public Property ProductID() As Integer
        Get
            Return mProductID
        End Get
        Set(ByVal value As Integer)
            mProductID = value
        End Set
    End Property

    ReadOnly Property Categories() As IEnumerable(Of Category)
        Get
            Dim list As New List(Of Category)
            list.Add(New Category With {.CategoryID = 1, .CategoryName = "Test1"})
            list.Add(New Category With {.CategoryID = 2, .CategoryName = "Test2"})

            Return list
        End Get
    End Property

End Class

Answer

tvanfosson picture tvanfosson · May 21, 2009

The dropdown list helper takes an IEnumerable<SelectListItem>, not an IEnumerable<Category>. Typically what you do is have your page have a particular model. The model includes a property for the selected value from the dropdown, not the collection. You supply the collection to select from via ViewData. You could have a view-only model that includes both the properties and the collection(s) to select from, but that might mean a proliferation of classes. There's some debate as to whether proliferating classes or magic strings are worse design.

My take on your code would be something like:

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of Foo)" %>
<asp:Content ID="aboutContent" ContentPlaceHolderID="MainContent" runat="server">
    <%=Html.DropDownList("Category", ViewData("CategoryList"))%>
</asp:Content>

Function Index() As ActionResult
    Dim list As New List(Of SelectListItem)
    list.Add(New SelectListItem  With {.Value = 1, .Text = "Test1"})
    list.Add(New SelectListItem With {.Value = 2, .Text = "Test2"})
    ViewData("CategoryList") = list
    Return View()
End Function

where Foo is a class that contains a property Category of type int.

If you wanted to do a strongly-typed view, then have Foo have a property Categories of type SelectListItem, then change:

<%=Html.DropDownList("Category", ViewData("CategoryList"))%>

to

<%=Html.DropDownList("Category", Foo.Categories )%>

and

    ViewData("CategoryList") = list
    Return View()

to

    Dim foo as New Foo
    foo.Categories = list
    Return View(foo)