I have a Generic List that it contains 4 value .
how can I get my generic list values by index ? I want to get all values in generic list
this is my code :
var Checked = (form.GetValues("assignChkBx")).ToList();
string str = "";
for (int i = 0; i < Checked.Count; i++)
{
str = str + Checked[i]. +",";
}
in this code I got all checkboxes values that checked . Now I want to get all values . how can I get values ?
Your question about the generic list is very misleading and I suspect that it has nothing to do with your real problem.
Depending on how you generated the checkboxes inside your view that might be possible or not. If you hardcoded them using directly an <input type="checkbox"> tag values of checkboxes that were not checked will never be sent to the server - that's how HTML checkboxes work. In this case you will not be able to get all values. If on the other hand you used the Html.CheckBoxFor helper to generate them then you will notice that this helper adds a hidden field to each checkbox in order to send all values. This helper operates on boolean values though.
So I would recommend you creating a view model that will contain 2 properties: one holding the values you are interested in and one boolean property indicating whether the user selected this value or not in the view:
public class ItemViewModel
{
public string Value { get; set; }
public bool Checked { get; set; }
}
and then have a view model which has collection of those items:
public class MyViewModel
{
public ItemViewModel[] Items { get; set; }
}
Now inside your view you can render those values like this:
#model MyViewModel
#using (Html.BeginForm())
{
for ( var i = 0; i < Model.Items.Length; i++)
{
<div>
#Html.CheckBoxFor(x => x.Items[i].Checked)
#Html.HiddenFor(x => x.Items[i].Value)
</div>
}
<button type="submit">OK</button>
}
and finally inside the controller action that this form will be submitted to you will be able to get all values:
[HttpPost]
public ActionResult SomeAction(MyViewModel model)
{
foreach (var item in model.Items)
{
// here you could use item.Checked and item.Value
}
...
}
Related
I have a many to many relationship which I have implemented and works perfectly, now I want to have a set of checkboxes on the "insert" page of one of the entities that can insert data in the intermediary table.
I have a ViewModel, which contains an array of the following class:
class CategoryManager {
public int id { get; set; }
public string Title { get; set; }
public bool Checked { get; set; }
}
On the View, I have the following Line to create the checkboxes for each of the array members:
for (int i = 0; i < Model.categories.Count(); i++)
{
#Html.CheckBoxFor(m => m.categories[i].Checked)<i> </i>
#Model.categories[i].Title <br/>
}
Now the thing I have noticed is when the page is submitted and the whole ViewModel is sent back to the controller, in the array of "CategoryManager"s, each instance has the ID and the "Checked" passe through properly (depending on whether the user marked the checkbox or not), however, the "Title" for all of them is NULL.
I know this should be a very basic thing I'm missing, but I am a newby ;)
Thank you very much in advance!
You don't generate a control for property Title (or for ID so not sure how you are posting that back back). Add a hidden input for the Title property
for (int i = 0; i < Model.categories.Count; i++)
{
#Html.CheckBoxFor(m => m.categories[i].Checked)
#Html.LabelFor(m => m.categories[i].Checked, "Model.categories[i].Title")
#Html.HiddenFor(m => m.categories[i].ID)
#Html.HiddenFor(m => m.categories[i].ID)
}
Note the use of LabelFor() to create a label associated with the checkbox.
I've asked this once before but without any code to look at, here I have an implementation and I'm wondering if there is a better way to accomplish this.
I want a repeating html section like so:
<div>
<input id=id1 name=id1 type=text/>
</div>
<div>
<input id=id2 name=id2 type=text/>
</div
etc
This could contain any number of input boxes which map to the List of 'something' classes I have in the model, I presently do this with a View
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Somethings.Count; i++)
{
Model.Index = i;
#Html.Action("Index", "HtmlSection", Model);
}
// other stuff
}
and a partial view
#{
int index = Model.Index;
}
#Html.TextBoxFor(x => x.Somethings[index].TheProperty)
Where the model looks like this
public class HtmlSectionModel
{
public List<Something> Somethings { get; set; }
public int Index { get; set; }
}
Finally the action looks like this
public ActionResult Index(HtmlSectionModel model)
{
// do stuff
}
To me this works but isn't ideal
The partial view can now only be used within this context, it uses the top level model rather than just the 'Something' class
I have to pass an index in the model in order to get unique name's for binding, if I didn't do this then textbox would have the same name/id
This seems to me to be a common pattern so others must have solved it in other ways?
I guess what I'm after here is the MVC equivalent of Asp.Net UserControls/Webcontrols (which seem to be child actions/partial views), but, combined with model binding which seems to require unique names
What I wanted can be accomplished with editor templates
Controller
public class UsesEditorController : Controller
{
[HttpGet]
public ActionResult Index()
{
return View(new SomeModel());
}
[HttpPost]
public ActionResult Index(SomeModel model)
{
return View(model);
}
}
Model
public class Blob
{
public string Name { get; set; }
public string Address { get; set; }
public Blob()
{
Name = string.Empty;
Address = string.Empty;
}
}
public class SomeModel
{
public List<Blob> Blobs { get; set; }
public SomeModel()
{
int count = 5;
this.Blobs = new List<Blob>(count);
for (int i = 0; i < count; i++)
{
this.Blobs.Add(new Blob());
}
}
}
View
#model MyProject.Areas.EditorTemplates.Models.SomeModel
#using (Html.BeginForm())
{
for (int i = 0; i < Model.Blobs.Count; i++)
{
#Html.EditorFor(m => m.Blobs[i], "CustomEditorForBlob");
}
<input type="submit" value="Send data back" />
}
And Editor, which can be anywhere in the view folder as I'm referring to it directly
#model MyProject.Areas.EditorTemplates.Models.Blob
#Html.TextBoxFor(m => m.Name)
#Html.TextBoxFor(m => m.Address)
This renders with ids like:
<input class="k-textbox" id="Blobs_1__Name" name="Blobs[1].Name" ...
So this gives me
List item
a repeating structure, just like UserControls in Asp.Net
The editor template only refers to the Blob class, it has no knowledge of the SomeModel class
Binding works (tested it)
It looks to me like what you are trying to accomplish is unique IDs for your inputs, and you certainly don't need a partial to do this. You can output your text box inside your for loop like the following:
#Html.TextBoxFor(x => x.Somethings[i].TheProperty)
This will generate a unique id something like id="Somethings_1_TheProperty". If you don't like that id, you can certainly make your own with something like this:
#Html.TextBoxFor(x => x.Somethings[i].TheProperty, new {id="id" + (i+1)})
Case:
I have a list of items of Class X displayed using Editor Template for Class X.
Problem:
How can I get index of an item being processed on the inside of the Editor Template?
I've been using this HtmlExtension that returns only the needed id of an iteration. It's basically a regex on ViewData.TemplateInfo.HtmlFieldPrefix that's capturing the last number.
public static class HtmlExtensions
public static MvcHtmlString Index(this HtmlHelper html)
{
var prefix = html.ViewData.TemplateInfo.HtmlFieldPrefix;
var m = Regex.Match(prefix, #".+\[(\d+)\]");
if (m.Success && m.Groups.Count == 2)
return MvcHtmlString.Create(m.Groups[1].Value);
return null;
}
}
Can be used in an EditorFor-template like this:
#Html.Index()
Use a for loop instead of for each and pass the indexer into the EditorFor extension; razor should handle the rest.
#for(var i = 0; i < Model.count(); i++)
{
#Html.EditorFor(m => Model.ToArray()[i], new { index = i })
}
Update:
pass in the the index of the item using view data as show above.
In your editor template access the item via the ViewBag
<span> Item Index: #ViewBag.index </span>
Using the EditorTemplate is the best solution when viewing models that contain a list of something.
In order to find the index for the sub-model being rendered you can use the property that Razor sets by default:
ViewData.TemplateInfo.HtmlFieldPrefix
Say, for example, you have the following view models:
public class ParagraphVM
{
public int ParagraphId { get; set; }
public List<LineVM> Lines { get; set; }
}
and
public class LineVM
{
public int Id { get; set; }
public string Text {get; set;}
}
and you want to be able to edit all the "LineVM" within a "ParagraphVM". Then you would use an Editor Template so you would create a view at the following folder (if it doesn't exist) with the same name as the sub-model Views/Shared/EditorTemplates/LineVM.cshtml:
#model MyProject.Web.MVC.ViewModels.Paragraphs.LineVM
#{
//this will give you the List's element like Lines[index_number]
var field = ViewData.TemplateInfo.HtmlFieldPrefix;
}
<div id="#field">
#Html.EditorFor(l => l.Text)
</div>
Assuming you have a Controller's ActionResult that is returning a View and passing a ParagrapghVM viewmodel to a view, for example Views/Paragraph/_Paragraph.cshtml:
#model MyProject.Web.MVC.ViewModels.Paragraphs.ParagraphVM
#using (Html.BeginForm("Details", "Paragraphs", FormMethod.Post))
{
#Html.EditorFor(p => p.Lines)
}
This view would render as many editors for the list Lines as items contains that list.
So if, for example, the property list ParagraphVM.Lines contains 3 items it would render something like:
<div id="#Lines[0]">
<input id="Lines_0__Text name="Lines[0].Text"/>
</div>
<div id="#Lines[1]">
<input id="Lines_1__Text name="Lines[1].Text"/>
</div>
<div id="#Lines[2]">
<input id="Lines_2__Text name="Lines[2].Text"/>
</div>
With that you can know exactly what position each items is within the list and for example use some javascript to create a carousel or whatever you want to do with it. But remember that to edit that list you don't really need to know the position as Razor takes care of it for you. If you post back the model ParagraphVM, the list Lines will have the values bound (if any) without any additional work.
How about:
#using System
#using System.Text.RegularExpressions
var i = Convert.ToInt32(Regex.Matches(
ViewData.TemplateInfo.HtmlFieldPrefix,
#"\[([0-9]+)?\]")[0].Groups[1].ToString());
I think the easiest way is:
#Regex.Match(ViewData.TemplateInfo.HtmlFieldPrefix, #"(?!\[)\d+(?=\])")
Or as helper:
public static string Index(this HtmlHelper html)
{
Match m = Regex.Match(html.ViewData.TemplateInfo.HtmlFieldPrefix, #"(?!\[)\d+(?=\])");
return m.Success ? m.Value : null;
}
Inspired by #Jona and #Ryan Penfold
You can use #Html.NameFor(m => m.AnyField). That expression will output the full name property including the index. You could extract the index there...
In my view
<%= Html.DropDownListFor( x => x.Countries[ i ], Model.CountryList )%>
in my controller
public int[ ] Countries { get; set; }
public List<SelectListItem> CountryList { get; set; }
When the forms gets posted there is no problem, the dropdown is populated and the values the user selects are posted. But when I try to load the form with already assigned values to the Countries[ ] it does not get selected.
Not sure if this is new to mv4 or if it exists in prior version. But the DropDownListFor includes an additional parameter for the SelectList Constructor.
SelectList(IEnumerable, String, String, Object)
For example:
Html.DropDownListFor( x => x.Countries[ i ], New SelectList(Model.CountryList,"ID","Description",Model.Countries[i]))
Where ID is the Country ID in the CountryList object and Description is the Country Name.
I'm getting the same too. When using foreach to loop around a DropDownListFor (i.e. to render multiple select elements on a page).
My work around is to set the selected value in the controller rather than the view: something like this:
In the controller:
public class FruitList
{
public int? selectedFruit{ get; set; }
public List<SelectListItem> fruits
{
get
{
fruitEntities F = new fruitEntities();
List<SelectListItem> list = (from o in F.Options
select new SelectListItem
{
Value = o.fruitID,
Text = o.fruit,
Selected = o.fruitID == selectedFruit
}).ToList();
return list;
}
}
}
public class ViewModel
{
public List<FruitList> collectionOfFruitLists { get; set; }
}
In the view
<table>
<% for (int i=0; i < Model.collectionOfFruitLists.Count; i++ )
{ %>
<tr>
<td><%: Html.DropDownList("fruitSelectList", collectionOfFruitLists[i].fruits, "Please select...") %></td>
</tr>
<%} %>
</table>
The nifty bit is Selected = o.fruitID == selectedFruit in the controller which acts like a SQL CASE statement; this is really well explained by Lance Fisher (thanks Lance, your post really helped me out :)
I know this question is a bit old but I just came across this problem with looping through a list of objects and attempting to bind the values to DropDownListFor(s) in my Edit View.
I overcame the issue with an inline solution by using the logic from some of the previous solutions given by others for this question.
Binding to my Model:
#Html.DropDownListFor(model => model.QuestionActions[i].QuestionActionTypeId,
Model.QuestionActionTypes.Select(x => new SelectListItem() { Value = x.Value, Text = x.Text, Selected = (x.Value == Model.QuestionActions[i].QuestionActionTypeId.ToString()) }).ToList(),
"Select Action Type",
new { })
Model.QuestionActionTypes is a SelectList that is populated in my Controller.
This is a bit of a hack, and JavaScript reliant but it worked very well for me.
You'll need to know the client IDs that will be produced by the fields (usually these can be worked out manually but for safety you may want to use something like a FieldIDFor method.
You just need to set the values based on the model, in jQuery's $(document).ready:
<script type="text/javascript">
$(document).ready(function () {
#for(var i = 0; i < 5; i++)
{
<text>
$("##Html.FieldIDFor(m => m.Countries[i])").val("#Model.Countries[i]");
</text>
}
});
</script>
A select box sends a single value, so the Countries property should not be an array. Also in your post it is not clear where's the i variable you are using in your lambda expression coming from and this x.CountryList used in the helper won't compile as x is not defined.
Model:
public class MyModel
{
public int SelectedCountry { get; set; }
public List<SelectListItem> CountryList { get; set; }
}
View:
<%= Html.DropDownListFor(x => x.SelectedCountry, Model.CountryList) %>
UPDATE:
According to the comment it seems that there are multiple drop downs. I suspect that the problem might come from the i variable used as index in a for loop.
You might try this instead:
Model:
public class MyModel
{
public int[] Countries { get; set; }
public List<SelectListItem> CountryList { get; set; }
}
View:
<% foreach (var country in Model.Countries) { %>
<%= Html.DropDownListFor(x => country, Model.CountryList) %>
<% } %>
Instead of using a IEnumerable in your viewmodel use a List of objects like this one:
public List<PairVM<string,string>> TiposValorCobertura { get; private set; }
And in your view when you assign the selectable elements for the dropdownlist in the loop, do it this way:
#Html.DropDownListFor(m => m.Coberturas[i].TipoLimiteInferior, new SelectList(Model.TiposValorCobertura,"Key","Description", Model.Coberturas[i].TipoLimiteIferior))
Where "Key" and "Description" are PairVM's properties
This problem has been driving me crazy for several hours now...
In my domain, I have 2 entities that are related to each other Sku and Item. Each sku can have many items.
public class Sku
{
private readonly EntitySet<Item> items;
public Sku()
{
items = new EntitySet<Item>(AttachItems, DetachItems);
}
public int SkuId { get; set; }
public string LongDescription { get; set; }
public EntitySet<Item> Items
{
get { return items; }
set{ items.Assign(value);}
}
private void AttachItems(Item entity)
{
entity.Sku = this;
}
private static void DetachItems(Item entity)
{
entity.Sku = null;
}
}
public class Item
{
public Sku Sku { get; set; }
public int ItemId { get; set; }
public string Category { get; set; }
public string Description { get; set; }
}
I am building a page that will allow the end-user to update some fields on the sku and some fields on each item at the same time.
<% using (Html.BeginForm("Save", "Merchant", FormMethod.Post,
new { enctype = "multipart/form-data" })) { %>
<fieldset>
<legend>Sku</legend>
<p><label for="SkuId">SkuId:</label>
<%= Html.TextBox("SkuId", Model.SkuId,
new{#readonly="readonly",onfocus="this.blur();"}) %></p>
<p><label for="LongDescription">LongDescription:</label>
<%= Html.TextBox("LongDescription", Model.LongDescription) %></p>
</fieldset>
<% for (int i = 0; i < Model.Items.Count; i++) { %>
<fieldset>
<legend>Item</legend>
<p><label for="ItemId">ItemId:</label>
<%= Html.TextBox(string.Format("items[{0}].{1}", i, "ItemId"),
Model.Items[i].ItemId,
new { #readonly = "readonly", onfocus = "this.blur();" })%></p>
<p><label for="Category">Category:</label>
<%= Html.TextBox(string.Format("items[{0}].{1}", i, "Category"),
Model.Items[i].Category)%></p>
<p><label for="Description">Description:</label>
<%= Html.TextBox(string.Format("items[{0}].{1}", i, "Description"),
Model.Items[i].Description)%></p>
</fieldset>
<%} // for-loop %>
<p><input type="submit" value="Save" /></p>
<%} // form %>
I have some controller code that works by accepting both a Sku and an EntitySet of Item and then assigning the Items to the Sku.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Sku sku, EntitySet<Item> items)
{
if (sku != null)
{
if (items != null)
{
sku.Items.Assign(items);
}
}
// save Sku to repository ...
// return Details view ...
}
This works, however I have noticed that it makes two trips through the DefaultModelBinder for each Item in addition to one trip for the Sku. When the Sku is bound, the setter for Items is called, and the binder even passes in a hydrated Items collection with the correct values. However, after the call to items.Assign, Items.Count is 0. This is why I have to re-assign the items in the controller code. I was expecting the items to be transferred over to the Items collection by the binder. This should eliminate the extra trip per item, since the items parameter on my controller method could be removed. Why isn’t this working?
You might need to create a custom model binder for this?
Hopefully, I am understanding you problem correctly...
Rather than defining your Save action with 2 parameters, have you tried just defining it with a single parameter, of type Sku?
You would then want to redefine the item HTML controls similar to the following example...
<%=
Html.TextBox
(
string.Format("sku.Items[{0}].{1}", i, "ItemId"),
Model.Items[i].ItemId,
new { #readonly = "readonly", onfocus = "this.blur();" }
)
%>
This way, you're populating the items directly in the Sku object itself.
Another potential solution would be to add an additional field to your Item class, such as...
Int32 SkuId { get; set; }
This way, you could define an additional hidden field for each item in your view that would be auto-bound to the SkuId of each item back at the controller.
<%=
Html.Hidden
(
string.Format("sku.Items[{0}].{1}", i, "SkuId"),
Model.Items[i].SkuId
)
%>
You could then just update your items collection independently of your Sku object. Regardless of which way you go, the two collections have to explicitly tell Linq to SQL to update the sku and items anyhow.
You could also define your own binder class, but that's probably more work than it's worth in this case. Just follow the ASP.NET MVC conventions, and I think you should be able to find something that will work without feeling like it's a hack.
I had a similar issue where EntitySets weren't bound properly in my ViewModel.
What I did was to create another property called Mvc[YourEntitySetPropertyName], as a generic list, that wrapped around the private fields of the EntitySet:
public List<InvoiceLineItem> MvcInvoiceLineItemList
{
get { return _invoiceLineItemList.ToList(); }
set { _invoiceLineItemList.AddRange(value); }
}
I then used that instead of the EntitySet property on my view markup.
There will be no need to pass the items in your controller method signature- just pass the Sku at that point:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Sku sku)
{
if (sku != null)
{
// save Sku to repository ...
// return Details view ...
}
}