0

I've been looking through Google search results for ways to implement some sort of RadioButton list (VB, MVC3, Razor). Unfortunately, most of the results I find are in C#; this is the most useful reference I can find: Jon Lanceley's MVC3 RadioButton Helper (C#, by the way), so I needed to translate the code into VB (thanks, developerFusion!).

I thought it would be fairly straighforward, but I discovered that the compiler doesn't recognize the function as part of the module namespace; some attempts at working around the problem ensued, but failed.

Here's my code (you can see all the attempts I've tried via comments, signed with Andrew.San if you Ctrl + F it):

Helper:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.Mvc
Imports System.Web.Mvc.Html
Imports System.Linq.Expressions
Imports System.Runtime.CompilerServices
Imports System.Text

Namespace RadioButtonListForHelperTest
    Public Module HtmlExtensions

        Sub New()

        End Sub

        <Extension()> _
        Public Function RadioButtonForSelectList(Of TModel, TProperty)( _
                                                                          htmlHelper As HtmlHelper(Of TModel), _
                                                                          expression As Expression(Of Func(Of TModel, TProperty)), _
                                                                          listOfValues As IEnumerable(Of SelectListItem)) As MvcHtmlString
            Dim metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData)
            Dim stringBuilder = New StringBuilder()

            If listOfValues IsNot Nothing Then
                ' Create a radio button for each item in the list 
                For Each item As SelectListItem In listOfValues
                    ' Generate an id to be given to the radio button field 
                    Dim id = String.Format("{0}_{1}", metaData.PropertyName, item.Value)

                    ' Create and populate a radio button using the existing html helpers 
                    Dim label = htmlHelper.Label(id, HttpUtility.HtmlEncode(item.Text))
                    Dim radio = htmlHelper.RadioButtonFor(expression, item.Value, New With { _
                     Key .id = id _
                    }).ToHtmlString()

                    ' Create the html string that will be returned to the client 
                    ' e.g. <input data-val="true" data-val-required="You must select an option" id="TestRadio_1" name="TestRadio" type="radio" value="1" /><label for="TestRadio_1">Line1</label> 
                    stringBuilder.AppendFormat("<div class=&quot;RadioButton&quot;>{0}{1}</div>", radio, label)
                Next
            End If

            Return MvcHtmlString.Create(stringBuilder.ToString())
        End Function
    End Module
End Namespace

Model:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.ComponentModel.DataAnnotations
Imports System.Web.Mvc

Namespace RadioButtonListForHelperTest.Models
    Public Class TestModel
        Public Property TestRadioList() As IEnumerable(Of SelectListItem)
            Get
                Return m_TestRadioList
            End Get
            Set(value As IEnumerable(Of SelectListItem))
                m_TestRadioList = value
            End Set
        End Property
        Private m_TestRadioList As IEnumerable(Of SelectListItem)
        Public Property TestRadioList2() As IEnumerable(Of SelectListItem)
            Get
                Return m_TestRadioList2
            End Get
            Set(value As IEnumerable(Of SelectListItem))
                m_TestRadioList2 = value
            End Set
        End Property
        Private m_TestRadioList2 As IEnumerable(Of SelectListItem)

        <Display( _
            Name:="Test Radio 1", _
            Description:="This is the first list.", _
            Prompt:="Some prompt message.", _
            Order:=2)> _
        <Required(ErrorMessage:="You must select an option for TestRadio")> _
        Public Property TestRadio() As String
            Get
                Return m_TestRadio
            End Get
            Set(value As String)
                m_TestRadio = value
            End Set
        End Property
        Private m_TestRadio As String

        <Display( _
            Name:="Test Radio 2", _
            Description:="This is the second list.", _
            Prompt:="Some prompt message.", _
            Order:=1)> _
        <Required(ErrorMessage:="You must select an option for TestRadio2")> _
        Public Property TestRadio2() As String
            Get
                Return m_TestRadio2
            End Get
            Set(value As String)
                m_TestRadio2 = value
            End Set
        End Property
        Private m_TestRadio2 As String

        ' I tried it out, just in case 
        ' (see Index; @Html.RadioButtonForSelectList).
        ' It didn't help, really.
        ' - Andrew.San
        Public Function RadioButtonForSelectList(Of TModel, TProperty)( _
                                                                          htmlHelper As HtmlHelper(Of TModel), _
                                                                          expression As Expressions.Expression(Of Func(Of TModel, TProperty)), _
                                                                          listOfValues As IEnumerable(Of SelectListItem)) As MvcHtmlString
            Return RadioButtonListForHelperTest.RadioButtonForSelectList(htmlHelper, expression, listOfValues)
        End Function
    End Class

    Public Class aTest
        Public Property ID() As Integer
            Get
                Return m_ID
            End Get
            Set(value As Integer)
                m_ID = value
            End Set
        End Property
        Private m_ID As Integer
        Public Property Name() As String
            Get
                Return m_Name
            End Get
            Set(value As String)
                m_Name = value
            End Set
        End Property
        Private m_Name As String
    End Class
End Namespace

Controller:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Web
Imports System.Web.Mvc
Imports RadioButtonListForHelperTest.RadioButtonListForHelperTest.Models

Namespace Controllers
    Public Class TestController
        Inherits System.Web.Mvc.Controller

        '
        ' GET: /Test

        Function Index() As ActionResult
            Dim list As New List(Of aTest)()
            list.Add(New aTest() With { _
             .ID = 1, _
             .Name = "Line1" _
            })
            list.Add(New aTest() With { _
             .ID = 2, _
             .Name = "Line2" _
            })
            list.Add(New aTest() With { _
             .ID = 3, _
             .Name = "Line3" _
            })
            Dim sl As New SelectList(list, "ID", "Name")

            Dim list2 As New List(Of aTest)()
            list2.Add(New aTest() With { _
             .ID = 1, _
             .Name = "test1" _
            })
            list2.Add(New aTest() With { _
             .ID = 2, _
             .Name = "test2" _
            })
            Dim sl2 As New SelectList(list2, "ID", "Name")

            Dim model = New TestModel()
            model.TestRadioList = sl
            model.TestRadioList2 = sl2

            model.TestRadio = "2"
            ' Set a default value for the first radio button helper
            Return View(model)
        End Function

        '
        ' POST: /Test

        <HttpPost()> _
        Public Function Index(model As TestModel, returnUrl As String) As ActionResult
            If ModelState.IsValid Then
                ModelState.AddModelError("", "Always force an error to be raised so we can test the postback sets the radio buttons to their last values.")
            End If

            ' If we got this far, something failed, redisplay form 
            Dim list As New List(Of aTest)()
            list.Add(New aTest() With { _
             .ID = 1, _
             .Name = "Line1" _
            })
            list.Add(New aTest() With { _
             .ID = 2, _
             .Name = "Line2" _
            })
            list.Add(New aTest() With { _
             .ID = 3, _
             .Name = "Line3" _
            })
            Dim sl As New SelectList(list, "ID", "Name")

            Dim list2 As New List(Of aTest)()
            list2.Add(New aTest() With { _
             .ID = 1, _
             .Name = "test1" _
            })
            list2.Add(New aTest() With { _
             .ID = 2, _
             .Name = "test2" _
            })
            Dim sl2 As New SelectList(list2, "ID", "Name")

            model.TestRadioList = sl
            model.TestRadioList2 = sl2

            Return View(model)
        End Function

    End Class
End Namespace

View:

@Imports RadioButtonListForHelperTest <!-- Andrew.San - Looked up Google on how to import a namespace in VB -->
@ModelType RadioButtonListForHelperTest.RadioButtonListForHelperTest.Models.TestModel

@Code
    Dim obj1 As System.Web.Mvc.HtmlHelper(Of RadioButtonListForHelperTest.RadioButtonListForHelperTest.Models.TestModel)
    Dim obj2 As System.Web.Mvc.HtmlHelper(Of RadioButtonListForHelperTest.RadioButtonListForHelperTest.Models.TestModel)
    ViewData("Title") = "Index"
End Code

<h2>Index</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@Using Html.BeginForm()
    @Html.ValidationSummary(True)
    @<fieldset>
        <legend>TestModel</legend>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.TestRadio)
        </div>
        <div class="editor-field">
            @Html.RadioButtonForSelectList(Function(model) model.TestRadio, Model.TestRadioList)
            <!-- Andrew.San
                Apparently, the method isn't a member of 
                System.Web.Mvc.HtmlHelper(Of RadioButtonListForHelperTest.RadioButtonListForHelperTest.Models.TestModel),
                which is odd; I don't see the need for a Model to have this method. 
            -->

            @RadioButtonListForHelperTest.RadioButtonListForHelperTest.RadioButtonForSelectList(obj1, Function(model) model.TestRadio, Model.TestRadioList)
            <!-- Andrew.San
                The compiler needs me to send an htmlHelper argument to the 
                HtmlExtension RadioButtonForSelectList method, 
                which Jon Lanceley didn't need to do.

                @Html.RadioButtonForSelectList(m => m.TestRadio, Model.TestRadioList)
            -->

            @Html.ValidationMessageFor(Function(model) model.TestRadio)
        </div>

        <div class="editor-label">
            @Html.LabelFor(Function(model) model.TestRadio2)
        </div>
        <div class="editor-field">
            <!-- Andrew.San
                As per the first radio button list. 
            -->
            @Html.RadioButtonForSelectList(Function(model) model.TestRadio2, Model.TestRadioList2)
            @RadioButtonListForHelperTest.RadioButtonListForHelperTest.RadioButtonForSelectList(obj2, Function(model) model.TestRadio2, Model.TestRadioList2)
            @Html.ValidationMessageFor(Function(model) model.TestRadio2)
        </div>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
End Using

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

Right. That's all I've got.

1 Answers1

0

The problem is that your extension method is in the RadioButtonListForHelperTest namespace, but in your view you attempt to access it view the System.Web.Mvc.Html namespace. @Html is in System.Web.Mvc.

To access your extension method in the same namespace you need to do the following.

Change your current namespace to System.Web.Mvc.Html. Then create a static class named whatever you like, MyExtensions and then include you extension method in this class. It must be static.

The Muffin Man
  • 19,585
  • 30
  • 119
  • 191
  • Thank you for recommended solution. Unfortunately, the accessibility of your suggested `Public Static Class MyExtensions` isn't valid per VB standard; `Public Module MyExtensions` will be accepted. Furthermore, `` only works in a `Module`. Reference linked **[here](http://stackoverflow.com/questions/881570/classes-vs-modules-in-vb-net)**. Although, somehow, I found my way to answering the problem... regardless of whether it's completely foolproof. I'll post it. – Andrew.San Dec 28 '12 at 08:16
  • I gave you enough to point you in the right direction. I didn't think I needed to hold your hand and give you exactly the code you needed. This is simply a namespacing issue. I apologize for providing C# code, but only real programmers use C#. – The Muffin Man Dec 28 '12 at 17:05