views:

39

answers:

2

I would like to pass the item list for a drop-down list into a strongly-typed partial view. The parent view uses a ViewModel class that contains the properties both for the value I want to set and the list of options the associated drop-down list should contain.

My ViewModel looks like:

public class EntityFormViewModel
{
    private MyDBEntity Entity { get; set; }

    // *** The list of options. ***
    public SelectList DataTypeSelectList { get; private set; }

    public int EntityId { get; set; }
    public byte FormatId { get; set; }

    // *** The property the options relate to. ***
    [UIHint("DataTypesDropDownST")]
    public string DataType { get; set; }
    // Other properties snipped...


    public EntityFormViewModel(EntityModel db, int Id)
    {
        Entity = db.MyDBEntity.First(e => e.EntityId == Id);
        DataTypeSelectList = new SelectList(db.SDDS_DataType.ToList(), "DataType", "Description", this);

        EntityId = Entity.EntityId;
        FormatId = Entity.FormatId;
        DataType = Entity.DataType;
        // Etc ...
    }

    public void Update(EntityModel db)
    {
        Entity.EntityId = EntityId;
        Entity.FormatId = FormatId;
        Entity.DataType = DataType;
        // Etc ...

        db.SaveChanges();
    }
};

My partial view in EditorTemplates/DataTypesDropDown.ascx looks like

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcApp.Controllers.EntityFormViewModel>" %>

<!-- Doesn't work. -->
<%= Html.DropDownListFor(m => Model, Model.DataTypeSelectList) %>

<!-- Doesn't work either. -->
<!-- <% Html.DropDownListFor(m => Model, Model.DataTypeSelectList); %> -->

And my parent view looks like

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcApp.Controllers.EntityFormViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Edit
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit</h2>

    <% using (Html.BeginForm()) {%>
        <%: Html.ValidationSummary(true) %>

        <fieldset>
            <legend>Fields</legend>

            <%: Html.EditorFor(m => m) %>

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

    <% } %>

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

</asp:Content>

I can get it to work fine without strong typing by passing the list of items in the ViewData dictionary, thus:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<%: Html.DropDownList("", new SelectList(ViewData["DataTypes"] as IEnumerable, "DataType", "Description", Model)) %>

but I get the following error with strong typing:

The model item passed into the dictionary is of type 'System.String', but this dictionary requires a model item of type 'MvcApp.Controllers.EntityFormViewModel'.

It looks like the wrong type (a string) is being passed into the partial view, but what can I do to change the type I pass? Or is my approach completely wrong? If so, how should it be done? I imagine this is a very common scenario.

I know I should use a "repository" class for database access, but I want to get this working before building too much other stuff around it. Note my ViewModel is in the controllers namspace. I guess I should move it to the Model namespace, but I doubt that's the cause of my problem.

=================================================================================

UPDATE

I still have a problem, but at least I solved the compiler error. I changed the view model as follows

    // Make this private
    private string DataType { get; set; }

    // Add this which supplies all the list information.
    [UIHint("DataTypesDropDownST")]
    public DataTypeOptsAndVal DataTypeAndOpts
    {
        get { return new DataTypeOptsAndVal(DataType, DataTypeSelectList); } 
        set { DataType = value.DataType; } 
    }

The idea being derived from the fact the return value of the property controls the type passed into the partial view; the whole model is not passed, which was my earlier mistake. Therefore, I return a type that contains everything the partial view needs. The new partial view is

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcApp.Controllers.DataTypeOptsAndVal>" %>

<%: Html.DropDownListFor(m => m.DataType, Model.DataTypeSelectList) %>

However, the form now renders with this field completely absent. I tried including static text in the partial view, but even this was not rendered.

Any help appreciated. Surely it isn't that hard to strongly type a list of options?

A: 

In your main view instead of:

<%: Html.EditorFor(m => m) %>

use:

<%: Html.EditorForModel() %>

It's equivalent but more readable.

As far as why the editor template is not rendered you used an UIHint attribute on your model. This means that it will look for ~/Views/Home/EditorTemplates/DataTypesDropDownST.ascx. Make sure this file is present at that location and contains the code you've shown in your update.

Darin Dimitrov
Is the code working now?
Darin Dimitrov
Darin, many thanks for taking the time to answer my question, and passing on the EditorForModel() tip. I have already updated the source code with your suggestion. My DataTypesDropDownST.ascx file is already in the location you suggest. I suspect that it's not being rendered because the associated property isn't a simple type, so the value returned from the drop-down can't be converted to the type of the property, even though the property only cares about the string.
Tim Williams