I'm not currently happy with the way that my DropDownListFor() objects are populated. I'm attempting to find as generic way of populating IEnumerable as possible. This is what I have so far.
Helper:
public static List<SelectListItem> ToSelectList(IDictionary<string,string> dictionaryItems, string selectedValue, string noSelection, bool search = false)
{
List<SelectListItem> items = new List<SelectListItem>();
if (search)
{
items.Add(new SelectListItem { Selected = true, Value = "-1", Text = string.Format("-- {0} --", noSelection) });
}
foreach (var item in dictionaryItems)
{
items.Add(new SelectListItem
{
Text = item.Key,
Value = item.Value,
Selected = selectedValue == item.Value ? true : false
});
}
return items
.OrderBy(l => l.Text)
.ToList();
}
Controller:
[HttpGet]
public ActionResult Index()
{
var model = new CreateModel();
var parentOrganisations = _orgs.FindBy(o => o.OwningOrganisationID == Globals.OrganisationID || o.ID == Globals.OrganisationID)
.OrderBy(o => o.OrganisationName);
Dictionary<string, string> items = new Dictionary<string, string>();
foreach (var item in parentOrganisations)
{
items.Add(item.OrganisationName, item.ID.ToString());
}
model.Organisations = SelectLists.ToSelectList(items, "-1", "-- None -- ", true);
return View(model);
}
View:
<div class="control-group">
<label class="control-label">Parent Organisation</label>
<div class="controls">
#Html.DropDownListFor(m => m.ParentOrganisationID, Model.Organisations, new { #class = "input-xlarge"})
<p class="help-block">Select a parent organisation to create a branch</p>
</div>
</div>
There seems to be A LOT of repetitive code in the controller. It takes a generic list, add's the Value and Text to a Dictionary and then uses that as input for a helper which builds up the select list to send as part of the model.
Does anyone have any better ways to achieve this? I hate having bloat in my controller, and when I get several drop downs on a form this is exactly what will happen in this instance.
Thanks,
EDIT - Thanks to Kenneth's helper method, I've now consolidated the whole thing into one call in the controller:
model.Organisations = _orgs.FindBy(o => o.OwningOrganisationID == Globals.OrganisationID || o.ID == Globals.OrganisationID)
.OrderBy(o => o.OrganisationName)
.ToList()
.ToSelectList(org => org.OrganisationName, org => org.ID.ToString(), "-1", "None", true);
You could provide callbacks that obtain the key and the value and then use those. Apart from that you can create it as an extension method:
Extension method:
public static List<SelectListItem> ToSelectList<T>(this List<T> Items, Func<T, string> getKey, Func<T, string> getValue, string selectedValue, string noSelection, bool search = false)
{
List<SelectListItem> items = new List<SelectListItem>();
if (search)
{
items.Add(new SelectListItem { Selected = true, Value = "-1", Text = string.Format("-- {0} --", noSelection) });
}
foreach (var item in Items)
{
items.Add(new SelectListItem
{
Text = getKey(item),
Value = getValue(item),
Selected = selectedValue == getValue(item) ? true : false
});
}
return items
.OrderBy(l => l.Text)
.ToList();
}
Usage:
List<Org>() parentOrganisations = // fetch here
model.Organisations = parentOrganisations.ToSelectList(org => org.ID.ToString(),
org => org.OrganisationName,
"-1",
"-- None -- ",
true);
Note: I typed this in the SO-editor, so you might have some syntax errors (they should be easy to solve though).
You can do in controller only like this: -
List<SelectListItem> dropdownItems = parentOrganisations
.Select(item => new SelectListItem
{
Value = item.ID.ToString(),
Text = item.OrganisationName,
Selected = "-1" == item.ID.ToString() ? true : false
})
.ToList();
Related
I have the following view that I am trying to build dropdownlist from ViewBag.Catagories:
#Html.DropDownListFor(x => x.CategoryId, List<SelectListItem>)ViewBag.Catagories,
new { #class = "form-control" })
Here, is the Action in which I have a ViewBag with a list:
public ActionResult CreateEdit(int id = 0)
{
var context = new FeedbackContext();
ViewBag.Catagories = context.Categories.Select(
x => new SelectListItem() { Text = x.Name, Value = x.Id.ToString() }).ToList();
if (id != 0)
{
return View(context.Tasks.FirstOrDefault(x => x.Id == id));
}
}
Here, it can populate the categories in the dropdownlist for the first time, but when I validate/resubmit it, it will throw an error.
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert type 'System.Data.Entity.Infrastructure.DbQuery<System.Web.Mvc.SelectListItem>' to 'System.Collections.Generic.List<System.Web.Mvc.SelectListItem>'
Please suggest why I am getting an error here .
I have an action that retrieves data and sends it to a view. In a view I have two dropdown menus.
First drop down shows salutation (such as "Mr.", "Ms.", etc.) and does not select value I sent for some reason. The other dropdown shows language list and correctly selects value I sent to the view. The relevant code in view is shown below.
#Html.DropDownListFor(model => model.Salutation, ViewBag.salutation as IEnumerable<SelectListItem>)
#Html.DropDownListFor(model => model.Language, ViewBag.languages as IEnumerable<SelectListItem>)
In the controller I have the following code to get the dropdown data.
ViewBag.salutation = new List<SelectListItem>() {
new SelectListItem() { Text = "", Value = "" },
new SelectListItem() { Text = "Mr.", Value = "Mr." },
new SelectListItem() { Text = "Ms.", Value = "Ms." },
new SelectListItem() { Text = "Mrs.", Value = "Mrs." }
};
and
var languages = (from l in db.Languages.ToList()
select new SelectListItem()
{
Text = l.Language,
Value = l.LanguageId.ToString()
}).ToList();
languages.Insert(0, new SelectListItem() { Text = "", Value = "" });
ViewBag.languages = languages;
The only difference I could think of is that the languages dropdown has an integer as value, whereas salutation dropdown has text as value. Is this why the salutation dropdown doesn't work? I know I could go through each salutation List<SelectListItem> item and set Selected property based on the value I retrieved from database. But I was hoping there would be a cleaner way to do this.
Any ideas?
Thanks
UPDATE
I decided to do what I did for another project.
IList<SelectListItem> _salutation = new List<SelectListItem>()
{
new SelectListItem() { Value = "", Text = "" },
new SelectListItem() { Value = "Mr.", Text = "Mr." },
new SelectListItem() { Value = "Ms.", Text = "Ms." },
new SelectListItem() { Value = "Mrs.", Text = "Mrs." }
};
// I could put the following in the declaration above, but for testing purposes it's in foreach loop.
foreach (var item in _salutation)
{
// compare to what's retrieved from database
item.Selected = item.Value == _viewData.Salutation;
}
ViewBag.salutation = _salutation;
After foreach loop I output .Value, .Selected property of each item in _salutation and I get all the correct values with one item being selected. Inside the view I did the following.
#foreach (var item in ViewBag.salutation as IEnumerable<SelectListItem>)
{
<b>#item.Value : #item.Text : #item.Selected</b><br />
}
All the correct Text/Values come up but none are Selected! This happens if I output the values after I execute #Html.DropDownListFor(). If I output the ViewBag.salutation before the html helper the correct value is selected.
SOLUTION
I found the following article useful: DropDownListFor with ASP.NET MVC.
Instead of using ViewBag I added the following to the ViewModel. (Showing the part for salutations drop down.)
public class TheViewModel
{
private IList<string> _salutations = new List<string>() { "", "Mr.", "Ms.", "Mrs." };
public IEnumerable<SelectListItem> SalutationItems
{
get
{
var salutations = _salutations.Select(s => new SelectListItem { Value = s, Text= s });
return salutations;
}
}
// The rest of the ViewModel
}
And in the View I have the following.
#Html.DropDownListFor(model => model.Salutation, Model.SalutationItems)
Instead of just supplying the list to the DropDownListFor helper you could provide it a SelectList. The SelectList constructor takes the list and allows you to explicitly set the selected value as well as an overload that lets you specify the Text and Value fields.
#Html.DropDownListFor(model => model.Salutation,
new SelectList(ViewBag.salutation as IEnumerable<SelectListItem>,
"Value", "Text", Model.Salutation))
Try this,
#Html.DropDownListFor(m =>m.DDCountryModel,IEnumerable<SelectListItem>)ViewBag.salutation)
#Html.DropDownListFor(model => model.Language, IEnumerable<SelectListItem>)ViewBag.languages)
Your Model should be like this,
public class Model
{
public IEnumerable<SelectListItem> DDCountryModel{ get; set; }
public IEnumerable<SelectListItem> Language{ get; set; }
}
Working on my first ASP.Net MVC2 web app recently, I came across some issues when I needed to select multiple values in a list box. I worked around it with some jQuery, but went ahead and put together some very simple code to demonstrate. I'm using EF for the model, with two entities - Customers and HelpDeskCalls:
Controller:
public ActionResult Edit(int id)
{
Customer currCustomer = ctx.Customers.Include("HelpDeskCalls").Where(c => c.ID == id).FirstOrDefault();
List<HelpDeskCall> currCustCalls = (ctx.HelpDeskCalls.Where(h => h.CustomerID == id)).ToList();
List<SelectListItem> currSelectItems = new List<SelectListItem>();
List<String> selectedValues = new List<string>();
foreach (HelpDeskCall currCall in currCustCalls)
{
bool isSelected = (currCall.ID % 2 == 0) ? true : false;
//Just select the IDs which are even numbers...
currSelectItems.Add(new SelectListItem() { Selected = isSelected, Text = currCall.CallTitle, Value = currCall.ID.ToString() });
//add the selected values into a separate list as well...
if (isSelected)
{
selectedValues.Add(currCall.ID.ToString());
}
}
ViewData["currCalls"] = (IEnumerable<SelectListItem>) currSelectItems;
ViewData["currSelected"] = (IEnumerable<String>) selectedValues;
return View("Edit", currCustomer);
}
View:
<div class="editor-field">
<%: Html.ListBoxFor(model => model.HelpDeskCalls, new MultiSelectList(Model.HelpDeskCalls, "ID", "CallTitle", (IEnumerable) ViewData["currSelected"]), new { size = "12" })%>
<%: Html.ListBoxFor(model => model.HelpDeskCalls, ViewData["currCalls"] as IEnumerable<SelectListItem>, new { size = "12"}) %>
<%: Html.ListBox("Model.HelpDeskCalls", new MultiSelectList(Model.HelpDeskCalls, "ID", "CallTitle", (IEnumerable)ViewData["currSelected"]), new { size = "12"}) %>
<%: Html.ValidationMessageFor(model => model.HelpDeskCalls) %>
</div>
For this sample, I'm just selecting HelpDeskCall.IDs which are even. I'm trying two different syntaxes for ListBoxFor: One uses an IEnumerable of values for selections, one using an IEnumerable of SelectListItems. By default, when I run this code, no selections are made to either ListBoxFor, but the non-strongly typed ListBox selects correctly.
I read this post on ASP.Net and this thread on SO, but no joy. In fact, if I add the override ToString() to my HelpDeskCall class (as suggested in the ASP.net thread) all values are selected, which isn't right either.
If someone could shed some light on how this should work (and what I'm missing or doing wrong), this then neophyte would be very grateful.
Here's an example illustrating the strongly typed version:
Model:
public class MyViewModel
{
public int[] SelectedItemIds { get; set; }
public MultiSelectList Items { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
// Preselect items with id 1 and 3
var selectedItemIds = new[] { 1, 3 };
var model = new MyViewModel
{
Items = new MultiSelectList(
new[]
{
// TODO: Fetch from your repository
new { Id = 1, Name = "item 1" },
new { Id = 2, Name = "item 2" },
new { Id = 3, Name = "item 3" },
},
"Id",
"Name",
selectedItemIds
)
};
return View(model);
}
}
View:
<%: Html.ListBoxFor(x => x.SelectedItemIds, Model.Items) %>
I don't know if this behaviour has changed in the RTM of MVC3 that I'm using, but it seems that selection and binding now works out of the box. The only catch is that the model should contain a property with the IDs, like that :
public class MyViewModel {
public int[] ItemIDs { get; set; }
}
Then the following in the view would work fine, both pre-selecting the correct values and binding correctly during post:
#Html.ListBoxFor(model => model.ItemIDs, (IEnumerable<SelectListItem>)(new[] {
new SelectListItem() { Value = "1", Text = "1" },
new SelectListItem() { Value = "2", Text = "2" }
}))
I have found better workaround. Usual way to preselect select list:
#Html.ListBoxFor(
model => model.Roles,
new MultiSelectList(db.Roles, "Id", "Name")
)
#Html.ValidationMessageFor(model => model.Roles)
Doesn't work.., never ever any option is selected, until:
public ActionResult Edit(int id)
{
var user = db.Users.Find(id);
// this is workaround for http://aspnet.codeplex.com/workitem/4932?ProjectName=aspnet
ViewData["Roles"] = user.Roles.Select(r => r.Id);
return View(user);
}
Selected Roles has to be stored in ViewData, to workaround nasty bug.
Another option is to take advantage of nameof, you could do something like this;
Html.ListBox(nameof(MainProjectViewModel.Projects), Model.Projects)
I have an edit page with a Html.DropDownList in it....I cant show the dropdownlist value it always shows up with Select instead i want to make the dropdown show an item as selected based on a model value say Model.Mes_Id... Any suggestion how it can be done...
<p>
<label for="MeasurementTypeId">MeasurementType:</label>
<%= Html.DropDownList("MeasurementType", // what should i give here?)%>
<%= Html.ValidationMessage("MeasurementTypeId", "*") %>
</p>
EDIT: It has the list items but i want to show a value selected in the edit view...
public ActionResult Edit(int id)
{
var mesurementTypes = consRepository.FindAllMeasurements();
ViewData["MeasurementType"] = mesurementTypes;
var material = consRepository.GetMaterial(id);
return View("Edit", material);
}
My repository method,
public IEnumerable<SelectListItem> FindAllMeasurements()
{
var mesurements = from mt in db.MeasurementTypes
select new SelectListItem
{
Value = mt.Id.ToString(),
Text= mt.Name
};
return mesurements;
}
Set the selected item when you create the IEnumerable<SelectListItem>.
Personally I would create a specialized viewmodel for the form but going by your code, do something like:
public ActionResult Edit(int id)
{
//Put this first
var material = consRepository.GetMaterial(id);
//pass in your selected item
var mesurementTypes = consRepository.FindAllMeasurements(material.MeasurementTypeId);
ViewData["MeasurementType"] = mesurementTypes;
return View("Edit", material);
}
Then change your repository method to something like:
public IEnumerable<SelectListItem> FindAllMeasurements(int selectedId)
{
var mesurements = from mt in db.MeasurementTypes
select new SelectListItem
{
Value = mt.Id.ToString(),
Text= mt.Name,
Selected = mt.Id == selectedId
};
return mesurements;
}
HTHs,
Charles
Have a look at this blog entry.
http://weblogs.asp.net/ashicmahtab/archive/2009/03/27/asp-net-mvc-html-dropdownlist-and-selected-value.aspx
Basically, you need to convert your mesurementTypes list/enumerable into a SelectList or IEnumerable<SelectListItem>.
I would recommend, if possible, upgrading to ASP.NET MVC2 and using Html.DropDownListFor()
You should be returning a SelectionList which can specify a selected item.
How to create a DropDownList with ASP.NET MVC
Assuming that Model.Mes_Id contais the selected value, you can do something like this
<%
var Measurements = new SelectList((IEnumerable)ViewData["MeasurementType"], "Id", "Name", Model.Mes_Id);
Response.Write(Html.DropDownList("measurement_type", Measurements, "Select"));
%>
Html.DropDownListFor didn't work for me So I got the poppy like this
(in Edit method )
CreatList(long.Parse(wc.ParentID.ToString()));
private void CreatList(long selected= 0)
{
SqlConnection conn = new SqlConnection(Config.ConnectionStringSimple);
conn.Open();
Category wn = new Category(conn);
CategoryCollection coll = new CategoryCollection();
Category.FetchList(conn, ref coll);
ViewBag.ParentID = GetList(coll, selected);
}
private List<SelectListItem> GetList(CategoryCollection coll, long selected)
{
List<SelectListItem> st = new List<SelectListItem>();
foreach (var cat in coll)
{
st.Add( new SelectListItem
{
Text = cat.Name,
Value = cat.ID.ToString(),
Selected = cat.ID == selected
});
}
SelectListItem s = new SelectListItem {
Text = Resources.lblSelect,
Value = "0"
};
st.Insert(0, s);
return st;
}
<div class="editor-label">
#Html.LabelFor(model => model.ParentID)
</div>
<div class="editor-field">
#Html.DropDownList("ddlCat", (List<SelectListItem>)ViewBag.ParentID)
#Html.ValidationMessageFor(model => model.ParentID)
</div>
OK, I've been Googling for hours and trying everything and can't get anything to work. I am learning MVC using Sharp Architecture and have generated some basic forms for creating Client objects. I want to fill the state drop down list with a list of US states and let the user pick from that list. I am able to populate the list and get the value back (to save the client) but when I go to edit the client, the client's current state is not selected. I have set the selected value in the SelectList:
<li>
<label for="Client_StateProvince">StateProvince:</label>
<div>
<%= Html.DropDownListFor(c=>c.Client.StateProvince, new SelectList(Model.StateProvinces, "id", "Name", Model.Client.StateProvince), "-- Select State --")%>
</div>
<%= Html.ValidationMessage("Client.StateProvince")%>
</li>
This does not seem to be good enough. What am I missing?
<%= Html.DropDownListFor(c => c.Client.StateProvince.Id,
new SelectList(Model.StateProvinces,
"id",
"Name",
Model.Client.StateProvince),
"-- Select State --")%>
This does it.
Hope this helps someone else.
~Lee
Worked perfectly for me thanx! I used it to set a parent relation on a subcategory:
<%= Html.DropDownListFor(
model => model.Category.ParentId,
new SelectList(Model.Categories,
"CategoryId",
"Name",
Model.Categories.Where(x => x.CategoryId == Model.Category.ParentId).Single()))%>
Jeroen
I did it this way. Works well.
Controller
IFarmModelInterface service2 = new FarmModelRepository();
ViewData["Farms"] = new SelectList(service2.GetFarmNames(), "id", "FarmName", "XenApp");
View
<%: Html.DropDownListFor(m => m.Farm, (ViewData["Farms"] as SelectList)) %>
public ActionResult AllUsers()
{
List<Users> users = userRep.GetUsers();
var listUsers = (from u in users.AsEnumerable()
select new SelectListItem
{
Text = u.UserName,
Value = u.UserId.ToString(),
Selected = (u.UserId==6)
}).AsEnumerable();
ViewBag.ListItems = listUsers;
//ViewBag.SelectedItem = 2;
return View();
}
In AllUsers.cshtml
<p>#Html.DropDownList("ListItems")</p>
<%= Html.DropDownListFor(c => c.Client.StateProvince, new SelectList(Model.StateProvinces, "Id", "Name")) %>
and override ToString() for StateProvince to return Id, i.e.:
return Id.ToString();
This works but is not a perfect solution...
Dennis
This is View - MVC Controller
#Html.DropDownListFor(m => m.Entity, new ABC.Models.DropDownPopulate().MyMethod, new { #class = "form-control input-inline input-medium" })
MyMethod Get Data List Bind With Dropdown Using SelectListItems
public List<SelectListItem> MyMethod
{
get
{
List<SelectListItem> dropdownList = new List<SelectListItem>();
var items = new DropDown_Bl().GetAll();
foreach (var item in items)
{
SelectListItem dropdownItem = new SelectListItem();
dropdownItem.Value = item.RnID.ToString();
dropdownItem.Text = item.Description;
dropdownList.Add(dropdownItem);
}
dropdownList.Insert(0, new SelectListItem() { Text = "Please Select", Value = "", Selected = true });
return dropdownList;
}
}
GetAll Method In Dropdown_Bl - Used to get data as list from database
public List<My_Table> GetAll()
{
var Items = _Context.MyRepository.GetMany(q => q.Area == "ASD").ToList();
return Items ;
}
public ActionResult AllUsers() {
List<Users> users = userRep.GetUsers();
var listUsers = (from u in users.AsEnumerable()
select new SelectListItem
{
Text = u.UserName,
Value = u.UserId.ToString(),
Selected = (u.UserId==6)
}).AsEnumerable();
// ViewBag.ListItems = listUsers;
ViewData["tempEmpList"]=listUsers;
return View(); }
Fill Dropdown
#Html.DropDownList("SelectedEmployee", new SelectList((IEnumerable) ViewData["tempEmpList"], "Id", "Name"))