1

I am quite confused with how to effectively use the Html.DropDownList helper for ASP.NET MVC.

Background: I have a 5-page form, which saves data to the form each time "Next" is clicked. Users may navigate back and forth between sections, so previous sections will already be pre-populated with previously-entered data.

This works for TextBoxes. But not DropDownLists. I have tried a load of different methods, including:

I have a ViewModel such taht I have got my lists and my Model (a LINQ-to-SQL generated class) as properties. eg:

public class ConsultantRegistrationFormViewModel
{
    public IConsultantRegistration ConsultantRegistration { get; private set; }

    public SelectList Titles { get; private set; }
    public SelectList Countries { get; private set; }
    public SelectList Currencies { get; private set; }
    public int CurrentSection { get; private set; }

    private ConsultantRegistrationFormViewModel(IConsultantRegistration consultantRegistration)
    {
        ConsultantRegistration = consultantRegistration;


        CurrentSection = 1;

        Titles = new SelectList(new string[] { "Mr", "Mrs", "Miss", "Ms", "Dr", "Sir" });
        Countries = new SelectList(countries.Select(q => q.Name));
        Currencies = new SelectList(currencies,"CurrencyCode","FriendlyForm");
    }
}

My Controller's Edit Action on GET looks like:

public class ConsultantRegistrationController : Controller
{
        public IConsultantRegistrationRepository ConsultantRegistrationRepository { get; private set; }
        public ICountryRepository CountryRepository { get; private set; }
    public IEnumerable<ICountry> Countries { get; private set; }

    public ConsultantRegistrationController()
    {
        ConsultantRegistrationRepository = RepositoryFactory.CreateConsultantRegistrationRepository();
        CountryRepository = RepositoryFactory.CreateCountryRepository();

        Countries = CountryRepository.GetCountries().ToArray();
    }


    public ActionResult Edit(Guid id, int sectionIndex)
    {
        IConsultantRegistration consultantRegistration = ConsultantRegistrationRepository.GetConsultantRegistration(id);

        SelectList bankBranchCountriesSelectList = new SelectList(Countries, "BankBranchCountry", "CountryName", consultantRegistration.BankBranchCountry);
        ViewData["bankBranchCountrySelectList"] = bankBranchCountriesSelectList;

        return View(new ConsultantRegistrationFormViewModel(consultantRegistration,sectionIndex,  Countries,Currencies));
    }
}

With my View doing:

        <%: Html.DropDownList("ConsultantRegistration.BankBranchCountry",ViewData["bankBranchCountrySelectList"] as SelectList) %>

This gives me the error:

DataBinding: 'IWW.ArrowPay.ConsultantRegistration.Data.Country' does not contain a property with the name 'BankBranchCountry'.

Which it does, have a look at the schema of this property:

public interface IConsultantRegistration
{
    Guid ID { get; set; }


    [DisplayName("Branch Country")]
    string BankBranchCountry { get; set; }

}

(My LINQ-to-SQL type ConsultantRegistration implemented IConsultantRegistration)

It seems that it is trying to bind to the wrong type, though?

If I use this in my view (and use my Controller's Countries property):

        <%: Html.DropDownList("ConsultantRegistration.BankBranchCountry ",Model.Countries,"(select a Country)") %>

I get the saved value fine, but my model doesn't update on POST.

And if I use this in my view:

        <%: Html.DropDownListFor(model=>model.ConsultantRegistration.BankBranchCountry ",Model.Countries,"(select a Country)") %>

I get the list, and it POSTs the selected value back, but does not pre-select the currently selected item in my model on the view.

So I have a bit of the solution all over the place, but not all in one place.

Hope you can help fill in my ignorance.

Community
  • 1
  • 1
Program.X
  • 7,250
  • 12
  • 49
  • 83

2 Answers2

1

Ok, I solved it. Proper hacky, but it gets the job done.

I'm using the ViewData in my view:

                <%: Html.DropDownList("bankBranchCountrySelectList", ViewData["bankBranchCountrySelectList"] as SelectList)%>

With the following in my controller:

        public ActionResult Edit(Guid id, int sectionIndex)
        {
            IConsultantRegistration consultantRegistration = ConsultantRegistrationRepository.GetConsultantRegistration(id);

            ViewData["bankBranchCountrySelectList"] = Countries.Select(q => new SelectListItem() { Text = q.Name, Value = q.Name, Selected = (q.Name.Trim().Equals(consultantRegistration.BankBranchCountry, StringComparison.InvariantCultureIgnoreCase)) }); // bankBranchCountriesSelectList;

            return View(new ConsultantRegistrationFormViewModel(consultantRegistration,sectionIndex,  Countries,Currencies));
        }



        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Edit(Guid id, int sectionIndex, FormCollection formValues)
        {
            IConsultantRegistration consultantRegistration = ConsultantRegistrationRepository.GetConsultantRegistration(id); 

            UpdateModel(consultantRegistration);
                    ViewData["bankBranchCountrySelectList"] = Countries.Select(q => new SelectListItem() { Text = q.Name, Value = q.Name, Selected = (q.Name.Trim().Equals(consultantRegistration.BankBranchCountry, StringComparison.InvariantCultureIgnoreCase)) });

            IEnumerable<RuleViolation> ruleViolations = consultantRegistration.GetRuleViolations(sectionIndex);
            if (ruleViolations.Count() == 0)
            {

// ...
            }
            else
            {
                ModelState.AddRuleViolations(ruleViolations);
                return View(new ConsultantRegistrationFormViewModel(consultantRegistration, sectionIndex, Countries, Currencies));
            }


    }

Not ideal and breaks clean coding. No idea why it works, but that seems to be what MVC is all about with "convention over configuration".

Program.X
  • 7,250
  • 12
  • 49
  • 83
  • So you added the creation of the drop down list to both the GET of the edit and the POST of the edit, right? I ran into the same thing with the same solution that I don't understand: http://stackoverflow.com/questions/2365824/strongly-typed-view-with-a-selectlist-for-dropdownlist-via-viewdata-type-mismatc – Cymen Apr 09 '10 at 19:15
  • 1
    I have sent a request for illumination to Phil Haack over at haacked.com. Hopefully, he can explain what is going on. I'll add another comment if he responds. – Cymen Apr 09 '10 at 19:23
0

This article was written with Multi-Select Lists in mind, but the principle applies equally to a single-selection drop down list:

http://www.stevefenton.co.uk/Content/Blog/Date/201002/Blog/How-To-Handle-Multiple-Select-Lists-In-ASP-NET-MVC/

Fenton
  • 241,084
  • 71
  • 387
  • 401
  • Thanks for that. That does look to be more or less how I am doing it, myself. So still stuck, I'm afraid. – Program.X Apr 09 '10 at 10:36
  • Is BankBranchCountry definitely populated before you call "SelectList bankBranchCountriesSelectList = new SelectList(Countries, "BankBranchCountry", "CountryName", consultantRegistration.BankBranchCountry);" – Fenton Apr 09 '10 at 10:49
  • Assuming it is how I think it works, yes. The Edit action gets the already persisted record from the repository/data-store - then deals with the SelectLists. Question is, does this run before the View - and does it matter? That I don't know. – Program.X Apr 09 '10 at 11:05
  • That may be the crux of it. Write out the value to the view just above the drop down, then you'll know... <%= consultantRegistration.BankBranchCountry %> – Fenton Apr 12 '10 at 07:10