Search code examples
asp.net-mvcdrop-down-menustrongly-typed-view

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


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

Solution

  • 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)