Passing Values from the SelectList Items Using ViewModel - asp.net-mvc

I'd like to get the values of the selected items in dropdownlists. I am saving the files into the database with the following code:
public ActionResult UploadDoc(IEnumerable<HttpPostedFileBase> files)
{
foreach (var file in files)
{
if (file != null && file.ContentLength > 0)
{
byte[] data = new byte[file.ContentLength];
file.InputStream.Read(data, 0, file.ContentLength);
Document doc = new Document
{
UploadedOn = DateTime.Now,
MimeType = file.ContentType,
UserName = User.Identity.Name,
Data = data,
FromLanguage = 1,
ToLanguage = 2
};
dbContext = new MedicalDb();
dbContext.Documents.Add(doc);
dbContext.SaveChanges();
}
}
return RedirectToAction("Index");
}
but, I'd also like to get the selected values from the dropdownlists so that I can populate the FromLanguage and ToLanguage properties of the documents. I guess I'd need a viewmodel, but don't know how to do it. New rows for document upload are added using jQuery and names of the ddls are "ddlFromLanguage1", "ddlFromLanguage2", "ddFromLanguage3", and "ddlToLanguage1", "ddlToLanguage2", "ddlToLanguage3", etc. Thanks in advance for any help.
<form action="UploadDoc" method="post" enctype="multipart/form-data">
<table id="tblUploadDocs">
<tr id="row1">
<td><input type="file" name="files" id="file1" /></td>
<td>Bu dilden</td>
<td>#Html.DropDownList("ddlFromLanguage1", ViewBag.Languages as SelectList)</td>
<td>şu dile çevrilecek</td>
<td>#Html.DropDownList("ddlToLanguage1", ViewBag.Languages as SelectList)</td>
</tr>
</table>
<br />
Yeni dosya ekleyin
<input type="submit" />
</form>

Any form that is posted back returns a FormCollection to the controller in addition to model related values.
For example
//In your view
#using (Html.BeginForm("CountrySelect", "Country", FormMethod.Post))
{
#Html.AntiForgeryToken()
<select name="country" id="country-select">
<option value="selector">Pick a Country</option>
<option value="England">England</option>
<option value="England">England</option>
</select>
}
//In controller
//This will get you the name of the selected country from your form
[HttpPost]
Public ActionResult CountrySelect(FormCollection formData)
{
string country = formData["country"].toString();
}

I think you need to look at good example and do the same or very similar to them.
Take a look at these:
ASP.NET MVC 3 Viewmodel Pattern
Implementing Dropdownlist on Asp.net MVC 3 from viewModel
These should get you going.
Please let me know if you don't succeed or if what I gave you was actually helpful.
Thanks

The solution:
The viewmodel:
public class CustomerDocUploadViewModel
{
public HttpPostedFileBase File { get; set; }
public int FromLanguage { get; set; }
public int ToLanguage { get; set; }
}
The view:
#model IList<Models.ViewModels.CustomerDocUploadViewModel>
...
<form action="UploadDoc" method="post" enctype="multipart/form-data">
<table id="tblUploadDocs">
<tr id="row1">
<td><input type="file" name="[0].File" /></td>
<td>Bu dilden</td>
<td>#Html.DropDownList("[0].FromLanguage", ViewBag.Languages as SelectList)</td>
<td>şu dile çevrilecek</td>
<td>#Html.DropDownList("[0].ToLanguage", ViewBag.Languages as SelectList)</td>
</tr>
</table>
<br />
<a id="lnkAdd" href="javascript:addRow();" style="margin:10px 0;">Yeni dosya ekleyin</a>
<input type="submit" />
</form>
and finally the action method in the controller:
[HttpPost]
public ActionResult UploadDoc(IList<CustomerDocUploadViewModel> docInfos)
{
for (int i = 0; i < docInfos.Count; i++)
{
if (docInfos.ElementAt(i).File != null && docInfos.ElementAt(i).File.ContentLength > 0)
{
byte[] data = new byte[docInfos.ElementAt(i).File.ContentLength];
docInfos.ElementAt(i).File.InputStream.Read(data, 0, docInfos.ElementAt(i).File.ContentLength);
// Save the file into the database
Document doc = new Document
{
UploadedOn = DateTime.Now,
MimeType = docInfos.ElementAt(i).File.ContentType,
UserName = User.Identity.Name,
Data = data,
FromLanguage = docInfos.ElementAt(i).FromLanguage,
ToLanguage = docInfos.ElementAt(i).ToLanguage
};
dbContext = new MedicalDb();
dbContext.Documents.Add(doc);
dbContext.SaveChanges();
}
}
return RedirectToAction("Index");
}

Related

Trying to Post a Model back to the Controller, but it creates a new model instead

Tpa class is my base model.
public class Tpa
{
public bool selected { get; set; }
public int Id { get; set; }
}
Data class creates a list of Tpa objects.
public class Data
{
public List<Tpa> Tpas { set; get; }
public Data()
{
this.Tpas = new List<Tpa>();
this.Tpas.Add(new Tpa()
{
selected = false ,
Id = 1,
});
this.Tpas.Add(new Tpa()
{
selected = false,
Id = 2,
});
this.Tpas.Add(new Tpa()
{
selected = true,
Id = 3,
});
}
}
This is my Get.
[HttpGet]
public virtual ActionResult Index()
{
var model = new Data();
return View(model);
}
This is my view.
#model TpaUpload_2.Models.Data
#using (Html.BeginForm(MVC.TpaUpload_2.Home.ReceiveID(), FormMethod.Post))
<table class="table">
<tr>
#for (int count = 0; count < Model.Tpas.Count; count++)
{
var item = Model.Tpas[count];
<tr>
<td>
<input type="checkbox"
name=#Html.Raw("'s" + count + "CheckBox'")
id=#Html.Raw("'s" + count + "CheckBox'")
#*checked="#(item.selected == true)"*# />
<label for=#Html.Raw("'s" + count + "CheckBox'" )></label>
<input type='hidden'
id=#Html.Raw("'s" + count + "CheckBox'" )
name='item.selected'
value=#Html.Raw("'"+item.selected+"'")/>
</tr>
</table>
<input type="submit" value="Submit" />
This my Post.
[HttpPost]
public virtual ActionResult ReceiveID(Data myData)
{
...
}
I'm trying to use the checkbox value to change the "selected" on the model, and post back the model.
The problem is after the Form is submitted to the Post, the program will construct a new Data object, instead of using the Data model passed to the controller.
What did I do wrong? Any help will be greatly appreciated.
Your constructing html with name attributes that have absolutely no relationship to you model. When you submit, the DefaultModelBinder first initializes your Data model (which means that 3 new Tpa objects are added to its Tpas property. It then tries to find name/value pairs in the form collection that match you model properties but there are none.
First you need to modify you constructor to include only the initialization of the list, and remove the adding of the new items
public class Data
{
public List<Tpa> Tpas { set; get; }
public Data()
{
Tpas = new List<Tpa>();
}
}
And add the items in the GET method
public virtual ActionResult Index()
{
var model = new Data();
model.Tpas .Add(new Tpa(){ selected = false, Id = 1 });
// add other items
return View(model);
}
Then you need to construct you view correctly using the strongly typed html helpers so that your form controls are correctly named in relationship to your model
<table class="table">
#for (int i = 0; i < Model.Tpas.Count; i++)
{
<tr>
<td>
#Html.HiddenFor(m => m.Tpas[i].Id)
#Html.CheckBoxFor(m => m.Tpas[i].selected)
#Html.LabelFor(m => m.Tpas[i].selected)
</td>
</tr>
}
</table>
This give your controls the correct name attribute for model binding, for example
<input type="hidden" name="Tpas[0].Id" ... />
<input type="hidden" name="Tpas[1].Id" ... />
<input type="hidden" name="Tpas[2].Id" ... />
I suggest you compare that with what your currently generating to understand the difference.
Note also your current html is invalid - you have multiple <tr> elements inside a <tr> elements and you need to include the hidden input for the Id property or else this will not post back and you will end up with 3 Tpa objects all with id = 0.

Dynamically adding controls in MVC4

I am currently working on creating an MVC4 application where I want controls to be generated automatically from the database rows.
I have the table in my database containing the questions and the control type in which it should be answered by the user.
I am just thinking of a logic like
Where I can get the database rows in a dataset and then foreach it, then checking the type of control it belongs and then creating the control in my View.
This is my Controller action:
Public ActionResult Index()
{
// get the rows from the table
foreach(Iterate the rows)
{
if(controllerType1)
{
//stmnts
}
if(controllerType2)
{
//stmnts
}
}
return View();
}
This is just an idea of how can we build the solution. If I am going in the right way please guide me, else I am eager to know the possibilities where I can build my solution in different ways :).
You can create a editor template and pass the control list as model to the template and in the template you can iterate that list to generate the control. As i have shown below.
1->Create a class for Control Information.
public class ControlInfo
{
public string ControlType { get; set; }
public string ControlID { get; set; }
public string ControlName { get; set; }
public string ControlValue { get; set; }
public string ControlLabel { get; set; }
public bool IsChecked { get; set; }
}
2->Create an Editor Template (Partial view with control name say CustomControl.cshtml) in \Views\Shared\EditorTemplates path.
#model List<MvcApplication2.Models.ControlInfo>
<table>
#foreach (MvcApplication2.Models.ControlInfo ControlInfo in Model)
{
<tr>
<td>#ControlInfo.ControlLabel</td>
<td>
#switch (ControlInfo.ControlType.ToLower())
{
case "textbox":
<input type="text" name="#ControlInfo.ControlName" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" />
break;
case "checkbox":
if (ControlInfo.IsChecked)
{
<input type="checkbox" name="#ControlInfo.ControlName" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" checked="checked" />
}
else
{
<input type="checkbox" name="#ControlInfo.ControlName" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" checked="checked" />
}
break;
default:
break;
}
</td>
</tr>
}
</table>
3->Create a model for main view (say HomeModel).
public class HomeModel
{
public List<ControlInfo> ControlList { get; set; }
public void PolulateControlList()
{
//You can fill this list from database.
// For example i have filled the list manually.
ControlList = new List<ControlInfo>();
ControlList.Add(new ControlInfo() {ControlType="TextBox",ControlName="tbox1", ControlID="tbox1", ControlLabel="Name", ControlValue="Martin" });
ControlList.Add(new ControlInfo() { ControlType = "CheckBox", ControlName = "cbox1", ControlID = "cbox1", ControlLabel="Is Correct", ControlValue = "Yes", IsChecked=true });
}
}
4->Consume the editor template in the main view as.
#model MvcApplication2.Models.HomeModel
#{
ViewBag.Title = "Home Page";
}
#Html.EditorFor(model=>model.ControlList,"CustomControl")
5-> Call the main view in the controller ( Index here).
public ActionResult Index()
{
HomeModel ModelObj = new HomeModel();
ModelObj.PolulateControlList();
return View(ModelObj);
}
Edit 1:
For getting the posted value you need to modify the Editor Templates as below. The model properties whose name is equal to the name of the control posted as name value collection , will get automatically binded by the model binder of the mvc frame work, so for each property in the control collection i have created hidden tags and one input tag for the input value.
#model List<MvcApplication2.Models.ControlInfo>
<table>
#{ var index = -1;}
#foreach (MvcApplication2.Models.ControlInfo ControlInfo in Model)
{
index++;
<tr>
<td>#ControlInfo.ControlLabel</td>
<td>
<input type="hidden" name="#("ControlList[" + index + "].ControlID")" value="#ControlInfo.ControlID" />
<input type="hidden" name="#("ControlList[" + index + "].ControlLabel")" value="#ControlInfo.ControlLabel" />
<input type="hidden" name="#("ControlList[" + index + "].ControlName")" value="#ControlInfo.ControlName" />
<input type="hidden" name="#("ControlList[" + index + "].ControlType")" value="#ControlInfo.ControlType" />
#switch (ControlInfo.ControlType.ToLower())
{
case "textbox":
<input type="text" name="#("ControlList["+index+"].ControlValue")" id="#ControlInfo.ControlID" value="#ControlInfo.ControlValue" />
break;
case "checkbox":
<input type="hidden" name="#("ControlList[" + index + "].ControlValue")" value="#ControlInfo.ControlValue" />
if (ControlInfo.IsChecked)
{
<input type="checkbox" name="#("ControlList[" + index + "].IsChecked")" id="#ControlInfo.ControlID" value="true" checked="checked" />
}
else
{
<input type="checkbox" name="#("ControlList[" + index + "].IsChecked")" id="#ControlInfo.ControlID" value="true" />
}
break;
default:
break;
}
</td>
</tr>
}
</table>
And in the main view you need to have form
#model MvcApplication2.Models.HomeModel
#{
ViewBag.Title = "Home Page";
}
#using(Html.BeginForm()){
#Html.EditorFor(model=>model.ControlList,"CustomControl")
<input type="submit" name="name" value="Submit" />
}
And in controller you need to have corresponding post method
[HttpPost]
public ActionResult Index(HomeModel ModelObj)
{
// Your logic..........
return View(ModelObj);
}
This ModelObj will have the posted values.

Post Model back to Server

My aim is to export the clicked table, to excel (csv). This requires taking the Model on the page, and posting it to the server. But its providing difficult as its a list.
How do I post the Model in the View, to the controller.
Below is my code
View:
#model List<NameSpace.Property>
#using (Html.BeginForm("Export","Excel", FormMethod.Post))
{
<input type="submit" class="submit" value="Submit" />
}
<table .... />
Controller:
public void Export(List<Property> list)
{
}
As you did not state whether your table displays static data or dynamic data I have provided both solutions:
STATIC:
For example, if you request a url (lets say): /home/index/1. The table data is retrieved for that id (of 1) and then displayed in a table in the view.
If the information in the table does not change then you do not need to post any information back to the server. You should perform a GET request rather than a POST request to retrieve the CSV.
You would need to change the view to accept a model containing the original id that was requested and to use an action link rather than a form:
#model MyTableViewModel
#Html.ActionLink("Click here to export", "Export", "Excel", new { id = Model.Id });
<table .... />
Where the view model might be:
public class MyTableViewModel
{
public int Id { get; set; }
public List<Customer> Customers { get; set; }
}
public class Customer
{
public string Name { get; set; }
public string Street { get; set; }
public string PostCode { get; set; }
}
Then your export controller action would be:
public FileContentResult Export(int id)
{
string csv = //Retrieve the information for the provided id and convert to csv format
return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "expostfilename.csv");
}
DYNAMIC:
The view model would no longer need the id so would become:
public class MyTableViewModel
{
public List<Customer> Customers { get; set; }
}
The view could be written:
#model MyTableViewModel
#using (Html.BeginForm("Export", "Home"))
{
<table>
<tbody>
#for (int i = 0; i < Model.Customers.Count; i++)
{
<tr>
<td>Name: #Html.TextBoxFor(m => m.Customers[i].Name)</td>
<td>Street: #Html.TextBoxFor(m => m.Customers[i].Street)</td>
<td>PostCode: #Html.TextBoxFor(m => m.Customers[i].PostCode)</td>
</tr>
}
</tbody>
</table>
<input type="submit" value="submit" />
}
And the controller export action:
public FileContentResult Export(MyTableViewModel vm)
{
string csv = //convert view model (vm) to csv
return File(new System.Text.UTF8Encoding().GetBytes(csv), "text/csv", "expostfilename.csv");
}
Controller Action
public void Export(List<Property> list)
{
}
Property Class
public class Property
{
public string Name { get; set; }
public string Street { get; set; }
public string Postcode { get; set; }
}
Html Table
<table id="my-table">
<tbody>
<tr>
<td>Chris</td>
<td>Awesome Street</td>
<td>DH9 4LD</td>
</tr>
<tr>
<td>Sean</td>
<td>Lame Street</td>
<td>DH8 4SR</td>
</tr>
</tbody>
</table>
Jquery
<script type="text/javascript">
var aData = {};
$('#my-table tbody tr').each(function(index, value) {
aData['list[' + index + '].Name'] = $(value).children().eq(0).text();
aData['list[' + index + '].Street'] = $(value).children().eq(1).text();
aData['list[' + index + '].Postcode'] = $(value).children().eq(2).text();
});
$.post("#Url.Action("Export")", aData, function(data) {
});
</script>
This should be enough to get you started. Hope this helps
Update
Alternatively if you did not want a Jquery specific solution, you could output your data in hidden fields
<input type="hidden" name="[0].Name" value="Chris" />
<input type="hidden" name="[0].Street" value="..." />
<input type="hidden" name="[0].Postcode" value="...." />
<input type="hidden" name="[1].Name" value="Sean" />
<input type="hidden" name="[1].Street" value="..." />
<input type="hidden" name="[1].Postcode" value="...." />
and this would get submitted in your form

MVC 4 Asp.net ,using File Upload code How to save images in Database

here is my view
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<table>
<tr>
<td>File :</td>
<td><input type="file" name="File" id="file" /> </td>
</tr>
<tr>
<td><input type="submit" name="submit" value="upload" /></td>
</tr>
Here is my Controller
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(Picture picture)
{
if (picture.File.ContentLength > 0)
{
var fileName = Path.GetFileName(picture.File.FileName);
var path = Path.Combine(Server.MapPath("~/Content/Images"), fileName);
picture.File.SaveAs(path);
}
return RedirectToAction("Index");
}
and Model:
namespace FileUpload.Models
{
public class Picture
{
public HttpPostedFileBase File { get; set; }
}
This code helps me to save image in my MVC project root Image folder , but I want to save it to my database . I have tried many tutorial but could not succeed yet ... '
I am Actually making the student form every student will register his picture.
Convert your image into bytes and then store it in your database
[HttpPost]
public ActionResult Index(Picture picture)
{
byte[] Image;
if (Request.Files["files"] != null)
{
using (var binaryReader = new BinaryReader(Request.Files["file"].InputStream))
{
Image = binaryReader.ReadBytes(Request.Files["files"].ContentLength);
}
Picture.File =Image;
}
return RedirectToAction("Index");
}
Model
public class Picture
{
public byte[] File { get; set; }
}
View For Displaying Image
if (Model.File != null)
{
string imageBase64 = Convert.ToBase64String(Model.File );
string imageSrc = string.Format("data:image/gif;base64,{0}", imageBase64);
<img src="#imageSrc" width="100" height="100" />
}

ASP.NET MVC Two file upload, different destinations

I'm trying to upload two different files into two different database fields on same form.
------------------------------------
reportid | name | image | template |
------------------------------------
this is the table look. So I want to upload files to image and template. My model:
public class Report
{
[Key]
public int ReportID { get; set; }
[Required]
public string Name { get; set; }
public byte[] Image { get; set; }
public byte[] Template { get; set; }
}
My Create method in controller:
public ActionResult Create(Report report, HttpPostedFileBase file, HttpPostedFileBase temp)
{
if (ModelState.IsValid)
{
if (file != null && file.ContentLength > 0)
{
using (MemoryStream ms = new MemoryStream())
{
file.InputStream.CopyTo(ms);
report.Image = ms.GetBuffer();
}
}
if (temp != null && temp.ContentLength > 0)
{
using (MemoryStream ms1 = new MemoryStream())
{
temp.InputStream.CopyTo(ms1);
report.Template = ms1.GetBuffer();
}
}
db.Reports.Add(report);
db.SaveChanges();
db.Configuration.ValidateOnSaveEnabled = true;
return RedirectToAction("Index");
}
And part of view concerning uploads:
<div class="editor-label">
<%:Html.LabelFor(model => model.Image) %>
</div>
<div class="editor-field">
<input type="file" id="fuImage" name="file" />
</div>
<div class="editor-label">
<%:Html.Label("Template") %>
</div>
<div class="editor-field">
<input type="file" id="temp" name="temp"/>
</div>
<p>
<input type="submit" value="Create" />
</p>
I'm quite stuck in this since I can not use IEnumerable<HttpPostedFileBase> files as a parameter in Create method because I need to save it in a different field, or can I? How should I approach this? Please help :S
Note: Image upload works fine.
Why not use IEnumerable<HttpPostedFileBase> ? You may use it like this.
[HttpPost]
public ActionResult Create(Report report, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
//Let's take first file
if(files.ElementAt(0)!=null)
{
var file1=files.ElementAt(0);
if (file1!= null && file1.ContentLength > 0)
{
//do processing of first file
}
}
//Let's take the second one now.
if(files.ElementAt(1)!=null)
{
var temp =files.ElementAt(1);
if (temp!= null && temp.ContentLength > 0)
{
//do processing of second file here
}
}
}
//Do your code for saving the data.
return RedirectToAction("Index");
}
EDIT : After seeing your View Markup in your EDIT.
The name of the file input element should be same as the parameter name in your action method. (files in this example)
#using (Html.BeginForm("Create", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
<b>File 1</b>
<input type="file" name="files" id="file1" />
<b>File 2</b>
<input type="file" name="files" id="file2" />
<input type="submit" />
}
This code assumes that read ONLY the first 2 entries from the collection.Since you wanted only 2 files, i hardcoded the indexes.
Phil has a nice blog post explaining about it very nicely.

Resources