views:

452

answers:

3

I am new to vb9 and the .NET MVC. I want to build a MVC helper function for which I pass a Entity Framework object and have it build a select. Generally speaking I tried something like this:

Public Function RenderSelect(ByVal helper As HtmlHelper, ByVal sSelectName As String, ByVal aItmes As Array, Optional ByVal sTitleKeyName As String = "name", Optional ByVal sValueKeyName As String = "id") As String

        ' open select
        For Each Object In aItmes
            ' here i would like to do something like:
            Dim OptionValue = Object.(sValueKeyName) 
            ' NOTE: I have a var with the property name
            Dim OptionTitle = Object.(sTitleKeyName)

            . then add the option structure to the select
        Next
        ' close select

        Return String
    End Function

However, it isn't working. It would be great to have a way to do this and pass the current entity object. The data types are beating me up. Thanks in advance.

A: 

What isn't working? Does your code run? Does it generate a string?

You've got at least one typo that will cause the generated string to not work in an HTML form. Should be <option value={0}>{1}</option>

Edit 2: To get the IDictionary(Of Object, String) from your EF objects, I would write partial class implementations that add a GetSelectOptions method to each of your objects. Or create an interface with that method in it that each of your EF objects implement. Then you'd just call the RenderSelect method and pass in EFObject.GetSelectOptions as the SelectOptions parameter.

Edit: Here's how I would do this. Make your calling code responsible for reading the key/value pairs from your EF object. Then your RenderSelect extension method can be much cleaner. You don't want your view helper methods to be dependent on the structure of your model objects. And you certainly don't want your helper method to be dependent on the fact that you're using EF objects.

    Public Function RenderSelect(ByVal helper As HtmlHelper, _
                                 ByVal name As String, _
                                 ByVal SelectOptions As IDictionary(Of Object, String), _
                                 ByVal SelectedKey As Object, _
                                 ByVal htmlAttributes As IDictionary(Of String, Object)) As String
        Dim result = <select name=<%= name %>/>
        Dim optElement As XElement
        For Each opt In SelectOptions
            optElement = <option value=<%= opt.Key.ToString %>><%= opt.Value %></option>
            If opt.Key.Equals(SelectedKey) Then
                optElement.@selected = "1"
            End If
            result.Add(optElement)
        Next
        If htmlAttributes IsNot Nothing Then
            For Each attr In htmlAttributes
                result.SetAttributeValue(attr.Key, attr.Value)
            Next
        End If
        Return result.ToString
    End Function

For a complete set of overloaded DropDownList functions in VB.NET, check out this file from the vbmvc.codeplex.com project from which the above code was copied and modified.

http://vbmvc.codeplex.com/sourcecontrol/changeset/view/19233?projectName=VBMVC#331689

That code has a return type of XElement, but just use result.ToString to get the string representation of the element if that's what you want.

Dennis Palmer
thanks for pointing that out. Maybe I should clarify...
DrydenMaker
great, so that is what I want to do, but I don't have a dictionary. I actually start with a Entity Framework object and I want to specify the object properties via string so I can do something like :<%= Html.RenderSelect("SelectName", item, "OptionValueProperty", "OptionTextProperty") %>
DrydenMaker
I do like the Xhtml element methods, but again they all require that you pass a dictionary, that would require me to pre-process my objects. This would bring me back to the base issue. I have an object with properties, and a string with the name of the property I want. Is it possible in VB to get a value from the object?
DrydenMaker
A: 

The ultimate shortcoming if VB seems is it's verbose introspection. If you have a string with the name of a property there is no good way to get the value of that property on a random object. Making something work requires touching each class. Dennis Palmer's method is probably the best way to allow for generic select creation.

So I pose the question: Will the Entity Framework team build something useful in for this in future versions?

DrydenMaker
A: 

Ok, so the introspection in 2.0 is poor, but it has improved in 3.5. So I all but gave up on this until I started messing around with GetType, and I stumbled on this:

Imports System.Runtime.CompilerServices
Imports System.IO

Public Module HtmlCtrlHelper
    <System.Runtime.CompilerServices.Extension()> _
    Public Function RenderSelect(ByVal helper As HtmlHelper, ByVal sName As String, ByVal Itmes As Object, Optional ByVal sValueName As String = "id", Optional ByVal sDisplayName As String = "name") As String
        Dim wOutputSrting As StringWriter = New StringWriter()
        Dim wHtml As HtmlTextWriter = New HtmlTextWriter(wOutputSrting)

        wHtml.RenderBeginTag(HtmlTextWriterTag.Select)

        wHtml.AddAttribute("name", sName)
        wHtml.AddAttribute("id", sName)

        For Each thing As Object In Itmes
            wHtml.RenderBeginTag(HtmlTextWriterTag.Option)
            wHtml.AddAttribute("value", thing.GetType().GetProperty(sValueName).GetValue(thing, Nothing).ToString())
            wHtml.Write(thing.GetType().GetProperty(sDisplayName).GetValue(thing, Nothing).ToString())
            wHtml.RenderEndTag()
        Next

        wHtml.RenderEndTag()

        Return wOutputSrting.ToString()
    End Function
End Module

Now I can just call it like <%=Html.RenderSelect(Model)%> on my view if I so wish. Then if the entity Model object doesn't have 'id' or 'name' attributes, I can specify them like:

<%=Html.RenderSelect(Model, "nameofvalue", "nameofdisplay")%>
DrydenMaker