Question

My MVC4 application has a ListBox and several buttons. When I select an item in the ListBox and press the Delete button and hit the breakpoint in the Delete() method, for some reason all properties of the model object passed to the Delete() method are null. Why is this?

Here is some code:

In the model:

public class EdiFileModel
{
    public SelectList EdiFileNames { get; set; }
    public string SelectedEdiFilename { get; set; }
...
}

In the view:

@model EdiSimulatorWebSender.Models.EdiFileModel
...

@Html.ListBoxFor(m => m.SelectedEdiFilename, new SelectList(Model.EdiFileNames, "Value", "Text", Model.EdiFileNames.SelectedValue), new { @Id = "EdiFileNames", @style = "width:Auto;height=Auto;maxHeight:200;" })

<form action="" method="post">
 <input type="submit" value="Send" name="action:Send" />
 <input type="submit" value="Delete" name="action:Delete" />
 <input type="submit" value="Refresh" name="action:Refresh" />
</form>

In the controller:

    [HttpPost]
    [MultipleButton(Name = "action", Argument = "Delete")]
    [ActionName("Index")]
    public ActionResult Delete(EdiFileModel ediFileModel)
    {
        string selectedFileName = ediFileModel.SelectedEdiFilename;
        ...
    }

Update

I would like also to be able to get on postback not only the Id of the selected item, but the list itself, so that I could use the Id to get the selected item. In my case, the ListBox contains names of the files in a particular folder. I want to be able to select a file, press the Delete button and delete the file. In the Delete() method, I get back the Id, but I would like to get also EdiFileNames.

This is the latest code I have:

Model:

public class EdiFileModel
{
    //public SelectList EdiFileNames { get; set; }
    public Dictionary<string, string> EdiFileNames { get; set; }
    public string SelectedEdiFileId { get; set; }
    public string ResponseContent { get; set; }
}

Controller:

    public ActionResult Index()
    {
        var ediFileModel = new EdiFileModel {EdiFileNames = GetAllEdiFiles()};
        return View(ediFileModel);
    }

    [HttpPost]
    [MultipleButton(Name = "action", Argument = "Delete")]
    [ActionName("Index")]
    public ActionResult Delete(EdiFileModel ediFileModel)
    {
        string selectedFileId = ediFileModel.SelectedEdiFileId;

        string selectedFileFullName = Path.Combine(SourcePath, ediFileModel.EdiFileNames[selectedFileId]);
        System.IO.File.Delete(selectedFileFullName);            
        return View(ediFileModel);
    }

View:

@using (Html.BeginForm())
{
    var ediFilesSelectList = new SelectList(Model.EdiFileNames, "Key", "Value", Model.SelectedEdiFileId);
    @Html.ListBoxFor(m => m.SelectedEdiFileId, ediFilesSelectList, new {@Id = "EdiFileNames", @style = "width:Auto;height=Auto;maxHeight:200;"})
    @Html.HiddenFor(model => model.EdiFileNames)
Was it helpful?

Solution

In answer to your first issue, you should be including all data that is required in the postback (the form submisson) within the form tags

Anything outside the tags won't be included as part of the post data

There is an MVC helper which you can use for this which makes it nice and easy:

@using(Html.BeginForm("x/y/z")) 
{ 
  // Html/Razor for your controls/inputs here
}

You also need to include any data you want to send back to the model in the postback - if you don't include it it won't be there. You can either get the data in the postback scenario (on the controller action) or pass it back in the post data by including it in the form.

If you want to hide it from the users view, you can make it a hidden element:

@Html.HiddenFor(m => m.SomeProperty)

The second issue you have is that the model binder uses reflection to create instances of your model types to rehydrate your model from the post data.

Full details can be read here: http://msdn.microsoft.com/en-us/magazine/hh781022.aspx

This means that any types that you use as part of your model need to have a parameterless constructor in order for the model binder to create an instance and inject the post data.

You are using SelectList on your model, this does not have a parameterless constructor and should really only be used to represent a selectable list of items in the view. The model binder can't instantiate an empty SelectList to fill with the post data because of this and therefore throws the observed error

In order to make this work, you should use another type that does have a parameterless constructor or create your own type which contains the fields you need. You can probably use KeyValuePair<K,V> to represent your selectable data, or you might want to just create an explicit class

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top