0
votes

I am having problem passing a viewmodel that contains a list of "files" from my view to my controller(action). The viewmodel seems to be passed to the actionresult with all the propetries, except the list (List Files). The list just stays null, even if the model contains files when i submit the form.

What do i do wrong? I will paste my c# code from my controller, model, viewmodel and my RazorHTML.

File-model:

public class File
{
    [Key]
    public int ID { get; set; }
    [Display(Name = "Filnamn")]
    public string Filename { get; set; }
    //[Display(Name = "Beskrivning")]
    //public string Description { get; set; }
    [Display(Name = "Filtyp")]
    public string ContentType { get; set; }
    public byte[] Data { get; set; }
    public int Size { get; set; }

    //public string CreatorId { get; set; }

    public string CreatorID { get; set; }
    [Required]
    [Display(Name = "Skapare")]
    [ForeignKey("CreatorID")]
    public virtual ApplicationUser Creator { get; set; }

    //public int DocumentId { get; set; }

    public int DocumentID { get; set; }
    [Required]
    [Display(Name = "Dokument")]
    [ForeignKey("DocumentID")]
    public virtual Document Document { get; set; }
}

Viewmodel:

public class CreateDocumentViewModel
{
    public CreateDocumentViewModel()
    {
        CheckboxUsers = new List<CheckBoxListUser>();
        CheckboxGroups = new List<CheckBoxListGroup>();
        CheckboxTags = new List<CheckBoxListTags>();
        Files = new List<File>();
    }

    [Display(Name = "Beskrivning")]
    public string Description { get; set; }
    [Display(Name = "Titel")]
    public string Name { get; set; }
    [DataType(DataType.MultilineText)]
    [Display(Name = "Markdown")]
    public string Markdown { get; set; }
    HttpPostedFileBase FileToAdd { get; set; }

    [Display(Name="Användare")]
    public List<CheckBoxListUser> CheckboxUsers { get; set; }
    [Display(Name = "Grupper")]
    public List<CheckBoxListGroup> CheckboxGroups { get; set; }
    [Display(Name = "Taggar")]
    public List<CheckBoxListTags> CheckboxTags { get; set; }
    [Display(Name = "Filer")]
    public ICollection<File> Files { get; set; }
    public string FilesJson { get; set; }
}

View:

    @model MarkdownManagerNew.Viewmodels.CreateDocumentViewModel

@{
    ViewBag.Title = "CreateDocument";
}

<h2>Nytt dokument</h2>


@Html.BeginForm("CreateFile", "User", new { model = Model, test = Model.Name, files = Model.Files }, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()

<div class="form-horizontal">
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })

    <div class="form-group">
        <div class="col-md-10">
            @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control", placeholder = "Titel" } })
            @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-10">
            @Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control", placeholder = "Beskrivning" } })
            @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
        </div>
    </div>

    <div class="row">
        <div class="col-sm-12">
            <div class="form-group col-md-6 markdownEditorForm">
                <div class="col-md-12">
                    @Html.EditorFor(model => model.Markdown, new { htmlAttributes = new { @class = "form-control markdownEditor" } })
                    @Html.ValidationMessageFor(model => model.Markdown, "", new { @class = "text-danger" })
                </div>
            </div>
            <div class="col-sm-6 markdownResult"></div>
        </div>
    </div>

    <div class="row">
        <dl class="dl-horizontal">
            <dt>
                @Html.DisplayNameFor(model => model.CheckboxGroups)
            </dt>

            @for (int i = 0; i < Model.CheckboxGroups.Count; i++)
            {
                <dd>
                    @Html.CheckBoxFor(x => x.CheckboxGroups[i].IsChecked)
                    @Html.HiddenFor(x => x.CheckboxGroups[i].ID)
                    @Html.HiddenFor(x => x.CheckboxGroups[i].Display)

                    @Html.DisplayFor(x => x.CheckboxGroups[i].Display)
                </dd>
            }


            <dt>
                @Html.DisplayNameFor(model => model.CheckboxUsers)
            </dt>

            @for (int i = 0; i < Model.CheckboxUsers.Count; i++)
            {
                <dd>
                    @Html.CheckBoxFor(x => x.CheckboxUsers[i].IsChecked)
                    @Html.HiddenFor(x => x.CheckboxUsers[i].ID)
                    @Html.HiddenFor(x => x.CheckboxUsers[i].Display)

                    @Html.DisplayFor(x => x.CheckboxUsers[i].Display)
                </dd>
            }

            <dt>
                @Html.DisplayNameFor(model => model.CheckboxTags)
            </dt>

            @for (int i = 0; i < Model.CheckboxTags.Count; i++)
            {
                <dd>
                    @Html.CheckBoxFor(x => x.CheckboxTags[i].IsChecked)
                    @Html.HiddenFor(x => x.CheckboxTags[i].ID)
                    @Html.HiddenFor(x => x.CheckboxTags[i].Display)

                    @Html.DisplayFor(x => x.CheckboxTags[i].Display)
                </dd>
            }

        </dl>
    </div>

    <div class="form-group">
        @Html.Label("File", new { @class = "control-label col-md-2" })
        <div class="col-md-10">
            <input type="file" id="File" name="upload" />
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Ladda upp fil" class="btn btn-default" />
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-10">
            @if (Model.Files.Count > 0)
            {
                <h3>@Html.DisplayNameFor(model => model.Files)</h3>
                <table class="table table-striped table-bordered">
                    <tr>
                        <th>Filename</th>
                        <th>Filetype</th>
                        <th>Size(bytes)</th>
                    </tr>

                    @foreach (var file in Model.Files)
                    {
                        <tr>
                            <td>@file.Filename</td>
                            <td>@file.ContentType</td>
                            <td>@file.Size</td>
                        </tr>
                    }

                </table>
            }
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-offset-1 col-md-10">
            <input type="submit" value="Skapa" class="btn btn-default" />
        </div>
    </div>
</div>


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

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Controller

[HttpPost]
    public ActionResult CreateFile(CreateDocumentViewModel viewModel)
    {
        var file = repo.CreateFile(upload, GetCurrentUser());
        viewModel.Files.Add(file);
        TempData["viewModel"] = viewModel;

        //return RedirectToAction("CreateDocument", file);
        return RedirectToAction("CreateDocument", new { files = viewModel.Files });

    }

-EDIT-

I have also tried adding editorfields for every property in every file like this:

@for (int i = 0; i < Model.Files.Count; i++)
{
    <div>

        @Html.LabelFor(x => x.Files[i].DocumentID)
        @Html.EditorFor(x => x.Files[i].DocumentID)
        @Html.ValidationMessageFor(x => x.Files[i].DocumentID)
        @Html.LabelFor(x => x.Files[i].CreatorID)
        @Html.EditorFor(x => x.Files[i].CreatorID)
        @Html.ValidationMessageFor(x => x.Files[i].CreatorID)
        @Html.LabelFor(x => x.Files[i].ID)
        @Html.EditorFor(x => x.Files[i].ID)
        @Html.ValidationMessageFor(x => x.Files[i].ID)
        @Html.LabelFor(x => x.Files[i].ContentType)
        @Html.EditorFor(x => x.Files[i].ContentType)
        @Html.ValidationMessageFor(x => x.Files[i].ContentType)
        @Html.LabelFor(x => x.Files[i].CreatorID)
        @Html.EditorFor(x => x.Files[i].CreatorID)
        @Html.ValidationMessageFor(x => x.Files[i].CreatorID)
        @Html.LabelFor(x => x.Files[i].Data)
        @Html.EditorFor(x => x.Files[i].Data)
        @Html.ValidationMessageFor(x => x.Files[i].Data)
        @Html.LabelFor(x => x.Files[i].Filename)
        @Html.EditorFor(x => x.Files[i].Filename)
        @Html.ValidationMessageFor(x => x.Files[i].Filename)
        @Html.LabelFor(x => x.Files[i].Size)
        @Html.EditorFor(x => x.Files[i].Size)
        @Html.ValidationMessageFor(x => x.Files[i].Size)
    </div>
}
2

2 Answers

0
votes

Your files are of type ICollection thus each file information needs to be present within the form to be able to be posted to the controller in the populated viewmodel. Currently you have no fields for this collection and such the ICollection is empty when posted to the controller. You can create an custom EditorFor to display the File data as well as post this data to the controller.

Please refer to this tutorial on how to do this:

http://blog.learningtree.com/en/editing-collections-in-asp-net-mvc/

In your code you are doing something similar for the checkboxes:

@for (int i = 0; i < Model.CheckboxUsers.Count; i++)
{
    <dd>
        @Html.CheckBoxFor(x => x.CheckboxUsers[i].IsChecked)
        @Html.HiddenFor(x => x.CheckboxUsers[i].ID)
        @Html.HiddenFor(x => x.CheckboxUsers[i].Display)

        @Html.DisplayFor(x => x.CheckboxUsers[i].Display)
    </dd>
}

Note the hidden fields, this is what is passing the data to the controller for the list of checkboxes. I would reccomend creating a custom EditorFor for the files. Or more simply repopulate the collection of files again in your controller from the model.

0
votes

But the Files collection is not in any inputs. They are basically just labels, therefore they will not post to server. Since they do not change by user, you can just load the files from DB again in your action.