0

In my MVC5 Code-First application, I am working on some Export to Excel Functionality using EPPlus. The View I'm working with is my ExportController - Index.cshtml view. The Model I'm attempting to loop through is my main Model, INV_Assets.

I saw the below posted in several similar questions (checkbox just for testing)

@foreach (var property in Model.GetType().GetProperties())
{
    @Html.Label(property.Name)
    @Html.CheckBox(property.Name)
}

but it is not rendering my Model Properties? For instance I have properties like [owner], [note], [description], etc, but the only thing rendered on my View are checkboxes titled "Capacity", "Count", and "Item"?

The following is my Controller Actions and my View:

ExportController - Index:

@using GridMvc.Html
@using System.Collections.Generic
@model  List<InventoryTracker.Models.INV_Assets>

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Export</h2>

@*@foreach (var prop in Model.GetType().GetProperties())
{
    @(Html.TextBox(prop.Name, prop.GetValue(Model, null)))
}*@

@*@foreach(var property in ViewData.ModelMetadata.Properties)
{
    <label>@(property.DisplayName??property.PropertyName)</label>
    @Html.Editor(property.PropertyName)
}*@

@foreach (var property in Model.GetType().GetProperties())
{
    @Html.Label(property.Name)
    @Html.CheckBox(property.Name)
}


<a href="/Export/Export" class="btn btn-default btn-sm noDecoration"><span class="glyphicon glyphicon-export"> Export</span></a>
<br />
<br />
<a href="/Export/ExportUsingEPPlus" class="btn btn-default btn-sm noDecoration"><span class="glyphicon glyphicon-export"> Export - EPPlus</span></a>

ExportController:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using InventoryTracker.DAL;
using OfficeOpenXml;


namespace InventoryTracker.Controllers
{
    public class ExportController : Controller
    {
        InventoryTrackerContext _db = new InventoryTrackerContext();

        // GET: Export
        public ActionResult Index()
        {
            var assetList = _db.INV_Assets.ToList();
            return View(assetList);
        }

        public ActionResult Export()
        {
            GridView gv = new GridView();
            gv.DataSource = _db.INV_Assets.ToList();
            gv.DataBind();
            Response.ClearContent();
            Response.Buffer = true;
            Response.AddHeader("content-disposition", "attachment; filename=InventoryAssets-" + DateTime.Now + ".xls");
            Response.ContentType = "application/ms-excel";
            Response.Charset = "";
            StringWriter sw = new StringWriter();
            HtmlTextWriter htw = new HtmlTextWriter(sw);
            gv.RenderControl(htw);
            Response.Output.Write(sw.ToString());
            Response.Flush();
            Response.End();

            return RedirectToAction("StudentDetails");
        }

        public ActionResult ExportUsingEPPlus()
        {

            //FileInfo newExcelFile = new FileInfo(output);
            ExcelPackage package = new ExcelPackage();
            var ws = package.Workbook.Worksheets.Add("TestExport");
            ws.Cells["A1"].Value = "Sample Export 1";


            var memoryStream = new MemoryStream();
            package.SaveAs(memoryStream);

            string fileName = "Exported-InventoryAssets-" + DateTime.Now + ".xlsx";
            string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";

            memoryStream.Position = 0;
            return File(memoryStream, contentType, fileName);

        }

    }

    //https://stackoverflow.com/questions/5796151/export-model-data-to-excel-mvc
    //https://landokal.wordpress.com/2011/04/28/asp-net-mvc-export-to-excel-trick/


}

What I'm attempting to do is get a multi-select list of all my INV_Assets properties, and then export only those properties using EPPlus to Excel.


EDIT:

Code for Model INV_Assets:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using GridMvc.DataAnnotations;
using System.Web.Mvc;
using InventoryTracker.Models;

namespace InventoryTracker.Models
{
    [GridTable(PagingEnabled = true, PageSize = 30)]
    public class INV_Assets 
    {
        // Setting GridColumn Annotations allows you to use AutoGenerateColumns on view to auto create the Grid based on the model.


        public int Id { get; set; }

        public int Model_Id { get; set; }
        [ForeignKey("Model_Id")]
        public virtual INV_Models Model { get; set; }

        [Required]
        public int Manufacturer_Id { get; set; }
        [ForeignKey("Manufacturer_Id")]
        public virtual INV_Manufacturers Manufacturer { get; set; }

        [Required]
        public int Type_Id { get; set; }
        [ForeignKey("Type_Id")]
        public virtual INV_Types Type { get; set; }

        [Required]
        public int Location_Id { get; set; }
        [ForeignKey("Location_Id")]
        public virtual INV_Locations Location { get; set; }

        public int Vendor_Id { get; set; }
        [ForeignKey("Vendor_Id")]
        public virtual INV_Vendors Vendor { get; set; }

        [Required]
        public int Status_Id { get; set; }
        [ForeignKey("Status_Id")]
        public virtual INV_Statuses Status { get; set; }

        public string ip_address { get; set; }

        public string mac_address { get; set; }

        [DataType(DataType.MultilineText)]
        public string note { get; set; }
        public string owner { get; set; }

        //[DataType(DataType.Currency)]
        //[DisplayFormat(DataFormatString="{0:C}", ApplyFormatInEditMode=true)]
        [DisplayFormat(DataFormatString = "{0:#,###0.00}", ApplyFormatInEditMode=true)]
        public decimal cost { get; set; }
        public string po_number { get; set; }

        [DataType(DataType.MultilineText)]
        public string description { get; set; }

        public int invoice_number{ get; set; }

        [Required]
        public string serial_number { get; set; }

        [Required]
        public string asset_tag_number { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime? acquired_date { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime? disposed_date { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime? verified_date { get; set; }

        [Required]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public DateTime created_date { get; set; }

        [Required]
        public string created_by { get; set; }

        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
        public DateTime? modified_date { get; set; }

        public string modified_by { get; set; }

        // Flag to specify if item is available? (Not signed out, not auctioned, recycled, etc.)
        //public bool available { get; set; }
    }
}
tereško
  • 58,060
  • 25
  • 98
  • 150
Analytic Lunatic
  • 3,853
  • 22
  • 78
  • 120
  • Please show us the code for the entity backing `INV_Assets`. – Daniel Gabriel Mar 03 '15 at 15:53
  • @DanielGabriel, Added code for `INV_Assets` model above in EDIT. – Analytic Lunatic Mar 03 '15 at 15:54
  • Thanks. Why is your code that renders text boxes commented out? As is, you are trying to render checkboxes for every public property including those that are not boolean. – Daniel Gabriel Mar 03 '15 at 15:58
  • Everything commented out is just attempts that were not working. I tried Checkboxes for testing out that sample code in other threads, but what I'm really trying to get is a Multi-Select List for each Property in the `INV_Assets` Model. – Analytic Lunatic Mar 03 '15 at 15:59
  • You don't want to have your Model be any sort of Enumerable. Have it be a single class with an Enumerable property on it. Not only does that make it easier when needing to add additional data to your Model, but there are other issues that can arise when trying to serialize/deserialilze from an Enumerable Model. – krillgar Mar 03 '15 at 17:00

1 Answers1

0

Try it like this:

@Html.ListBox("PropertyList", typeof(INV_Assets).GetProperties().Select(p => new SelectListItem { Text = p.Name, Value = p.Name, Selected = false })

EDIT:

Changed the type of the object on which GetProperties() is called. Before it was called on your Model which is a list. But what you are really trying to do is call it on the object type stored in the list.

EDIT 2:

Changed to ListBox instead of ListBoxFor. Since you are not using a view model, you will need to extract the selected items from the Request on POST. You could also add a List<string> propertyList parameter to your post action and it should bind automatically.

EDIT 3:

To get the data in a post action, you have to wrap your list box in a form, like this:

@using (Html.BeginForm("ExportProps", "Export") {
    @Html.ListBox(...)
}

Here is how to get the submitted data in the POST action:

[HttpPost]
public ActionResult ExportProps(List<string> propertyList) {
    // do stuff with your data here
}

The above code is not tested, but it should give an idea of how to handle the post.

Daniel Gabriel
  • 3,939
  • 2
  • 26
  • 37
  • `@Html.ListBoxFor(Model.GetProperties().Select(p => new SelectListItem { Text = p.Name, Value = p.Name, Selected = false }))` Fails with `'System.Collections.Generic.List' does not contain a definition for 'GetProperties' and no extension method 'GetProperties' accepting a first argument of type 'System.Collections.Generic.List' could be found (are you missing a using directive or an assembly reference?)`... – Analytic Lunatic Mar 03 '15 at 16:15
  • Regarding the above EDIT - VS2013 will not even accept. It says no overload method of `ListBoxFor` takes 1 argument: `@Html.ListBoxFor(typeof(INV_Assets).GetProperties().Select(p => new SelectListItem { Text = p.Name, Value = p.Name, Selected = false }))` – Analytic Lunatic Mar 03 '15 at 16:27
  • Thanks. This is generating a ListBox (though small in height). Is there a way to make the list expandable or set the Height? The more I look at it, the more I like the idea of a group with checkboxes for each property. I found the following `CheckBoxListFor` but I can't figure out how to reference it on my view after installing via Nuget: http://mvccbl.com/ – Analytic Lunatic Mar 03 '15 at 17:09
  • I'm not sure about that control since I haven't used it before. They do have a documentation page though where I think they describe how to use it. – Daniel Gabriel Mar 03 '15 at 17:14
  • 1
    If you do want to use the `ListBox` from MVC, you can add a 3rd parameter with a class name like this: `@Html.ListBox("PropertyList", typeof(INV_Assets).GetProperties().Select(p => new SelectListItem { Text = p.Name, Value = p.Name, Selected = false }, new {@class="listbox"})` and then define the "listbox" class in your css and specify the height and other things as needed. If you find the above answer useful, please mark it as an answer. Thanks! – Daniel Gabriel Mar 03 '15 at 17:17
  • `@Html.ListBox("PropertyList", typeof(InventoryTracker.Models.INV_Assets).GetProperties().Select(p => new SelectListItem { Text = p.Name, Value = p.Name, Selected = false }, new {@class="listbox"}))` yields "No overload method for 'Select' takes 2 arguments".... – Analytic Lunatic Mar 03 '15 at 19:16
  • 1
    The parenthesis is in the wrong place. It should be: @Html.ListBox("PropertyList", typeof(InventoryTracker.Models.INV_Assets).GetProperties().Select(p => new SelectListItem { Text = p.Name, Value = p.Name, Selected = false }), new {@class="listbox"}) – Daniel Gabriel Mar 03 '15 at 19:19
  • That did it, thanks. Could you add another Edit with an example of doing the POST action for the passing the selected values? – Analytic Lunatic Mar 03 '15 at 19:46
  • @analytic, if this is the answer you were looking for, please mark it as such. It will help others when they search for the same thing. Thanks. – Daniel Gabriel Mar 05 '15 at 21:04
  • Thanks, I ended up going a slightly different route for the `ListBox`: https://stackoverflow.com/questions/28881503/pass-all-selected-multiselectlist-values-to-mvc-controller-for-epplus-export-to?noredirect=1#comment46024627_28881503 – Analytic Lunatic Mar 05 '15 at 21:08