I am trying to populate the selected value field of an MVC drop down list, but I have been unsuccessful. I am using MVC 5.1.
Here is a screenshot of the selected value and the model showing sales receipts:
Instead of sales receipts being selected, the model defaults to estimates:
When I use ViewData, the drop down works successfully:
Html.DropDownList("JMASettings.InvoiceMode")
Here is my code:
public void GetInvoiceMode(JMASettings model)
{
Dictionary<string, string> modes = new Dictionary<string, string>();
modes.Add("Estimates", "Estimates");
modes.Add("Invoices Only (Payments If Paid)", "Invoices");
modes.Add("Invoices No Payments", "InvoicesNoPayments");
modes.Add("Sales Receipts", "Sales Receipts");
if (Session["dataSource"] != null && Session["dataSource"].ToString() == "QBD")
modes.Add("Sales Orders", "Sales Orders");
IList<SelectListItem> ilSelectList = new List<SelectListItem>();
foreach (KeyValuePair<string, string> mode in modes)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Text = mode.Key;
selectListItem.Value = mode.Value;
if (model.InvoiceMode == mode.Value)
selectListItem.Selected = true;
else if (String.IsNullOrEmpty(model.InvoiceMode) && mode.Key == "Sales Receipts")
selectListItem.Selected = true;
ilSelectList.Add(selectListItem);
}
ViewData["JMASettings.InvoiceMode"] = ilSelectList;
}
Keep the property which represents the selected item on your view model like this.
public class CreateOrderVM
{
public List<SelectListItem> Modes {set;get;}
public string SelectedMode {set;get;}
public CreateOrderVM()
{
this.Modes=new List<SelectListItem>();
}
}
Now when you have to render the view, load the Modes collection and set the SelectedMode to whatever you want
public ActionResult Show()
{
var vm=new CreateOrderVM();
vm.Modes = GetModes();
//Set one of them as selected
vm.SelectedMode="B";
return View(vm);
}
private List<SelectListItem> GetModes()
{
var list=new List<SelectListItem>();
//hard coded for demo.Replace with actual values
list.Add(new SelectListItem { Value="A", Text="A"});
list.Add(new SelectListItem { Value="B", Text="B"});
list.Add(new SelectListItem { Value="C", Text="C"});
return list;
}
In your view,
#model YourNamespace.CreateOrderVM
#Html.DropdownListFor(s=>s.SelectedMode,Model.Modes,"Select")
Related
I'm attempting to set the selected value of a combobox depending on what the controller has set.
My DropDownListFor:
#if (Model.FormType == "Detailed")
{
#Html.DropDownListFor(model => model.FormType,
new SelectList(Enum.GetValues(typeof(FormType))),
"Detailed")
}
else
{
#Html.DropDownListFor(model => model.FormType,
new SelectList(Enum.GetValues(typeof(FormType))),
"Summary")
}
My FormType enum:
public enum FormType
{
Summary,
Detailed
}
When I try this, it's duplicating "Summary" again and selecting it so I end up with duplicate entries:
What am I doing wrong? Thank you!
Your "Detailed" or "Summary" just a title of dropdown list. You can change it to "Select" for example. But if you need to show a selected item :
You need an util to create a SelectList
public static SelectList GetSelectList(string selectedText)
{
Array values = Enum.GetValues(typeof(FormType));
List<SelectListItem> items = new List<SelectListItem>();
foreach (var i in values)
{
var item=new SelectListItem
{
Text = Enum.GetName(typeof(FormType), i),
Value = i. ToString()
};
if(item.Text==selectedText) item.Selected=true;
items.Add(item);
}
return new SelectList(items);
}
Add SelectList to your view model:
public Model ()
{
..... your code
public SelectList SelectList {get; set;}
{
after this you can put this code in your action
model.SelectList=GetSelectList(model.FormType);
and finaly the view:
#Html.DropDownListFor(model => model.FormType, model.SelectList,"Select")
This is something that has always puzzled me as to the best way round, while keeping maintainable code. The below code sets up a list of months and years for a payment gateway form, before assigning these to a variable of type List<SelectListItem>.
Intial Action
PayNowViewModel paymentGateway = new PayNowViewModel();
List<SelectListItem> paymentGatewayMonthsList = new List<SelectListItem>();
List<SelectListItem> paymentGatewayYearsList = new List<SelectListItem>();
for (int i = 1; i <= 12; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayMonthsList.Add(selectListItem);
}
int year = DateTime.Now.Year;
for (int i = year; i <= year + 10; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayYearsList.Add(selectListItem);
}
paymentGateway.ExpiryMonth = paymentGatewayMonthsList;
paymentGateway.ExpiryYear = paymentGatewayYearsList;
return View(paymentGateway);
It's a fair bit of code, and I find myself repeating this code, in similar formats to re-setup the dropdown lists options should the ModelState.IsValid be false and I want to return back to the view for the user to correct there mistakes.
HttpPost Action - Code
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmPayment(PayNowViewModel paymentGatewayForm, FormCollection form)
{
if (ModelState.IsValid)
{
// Post processing actions...
return View();
}
else
{
for (int i = 1; i <= 12; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayMonthsList.Add(selectListItem);
}
int year = DateTime.Now.Year;
for (int i = year; i <= year + 10; i++)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = i.ToString();
selectListItem.Text = i.ToString("00");
paymentGatewayYearsList.Add(selectListItem);
}
form.ExpiryMonth = paymentGatewayMonthsList;
form.ExpiryYear = paymentGatewayYearsList;
return View("MakePayment", form);
}
}
What's the best way to centralise this dropdown setup code so its only in one place? At present you'll see a large proportion (the for loops), is exactly repeated twice. A base controller with function? Or is it better to re-setup like the above?
Any advice appreciated!
Mike.
Add a private method to your controller (the following code assumes your ExpiryMonth and ExpiryYear properties are IEnumerable<SelectListItem> which is all that the DropDownListFor() method requires)
private void ConfigureViewModel(PayNowViewModel model)
{
model.ExpiryMonth = Enumerable.Range(1, 12).Select(m => new SelectListItem
{
Value = m.ToString(),
Text = m.ToString("00")
});
model.ExpiryYear = Enumerable.Range(DateTime.Today.Year, 10).Select(y => new SelectListItem
{
Value = y.ToString(),
Text = y.ToString("00")
});
}
and then in the GET method
public ActionResult ConfirmPayment()
{
PayNowViewModel model = new PayNowViewModel();
ConfigureViewModel(model);
return View(model);
}
and in the POST method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmPayment(PayNowViewModel model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model);
return View(model);
}
.... // save and redirect (should not be returning the view here)
}
If the set of your dropdown options is fixed (or recompilation is OK after the potential options change), you can use an enum to store your options.
public enum Month {
// if the dropdown is not required, add default value 0
Optional = 0,
[Display(Name = #"Month_January")]
January = 1,
[Display(Name = #"Month_February")]
February = 2,
// etc ..
}
To render this as a dropdown use an EditorTemplate Enum.cshtml:
#model Enum
#{
var enumType = ViewData.ModelMetadata.ModelType;
var allValues = Enum.GetValues(enumType).Cast<object>().ToSelectList(Model);
// read any attributes like [Required] from ViewData and ModelMetadata ...
var attributes = new Dictionary<string, object>();
}
#Html.DropDownListFor(m => m, allValues, attributes)
The ToSelectList extension method loops over all enum values and converts them to SelectListItems:
public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> list) {
return ToSelectList<T>(list, list.FirstOrDefault());
}
public static IList<SelectListItem> ToSelectList<T>(this IEnumerable<T> list, T selectedItem) {
var items = new List<SelectListItem>();
var displayAttributeType = typeof(DisplayAttribute);
foreach (var item in list) {
string displayName;
// multi-language:
// assume item is an enum value
var field = item.GetType().GetField(item.ToString());
try {
// read [Display(Name = #"someKey")] attribute
var attrs = (DisplayAttribute)field.GetCustomAttributes(displayAttributeType, false).First();
// lookup translation for someKey in the Resource file
displayName = Resources.ResourceManager.GetString(attrs.Name);
} catch {
// no attribute -> display enum value name
displayName = item.ToString();
}
// keep selected value after postback:
// assume selectedItem is the Model passed from MVC
var isSelected = false;
if (selectedItem != null) {
isSelected = (selectedItem.ToString() == item.ToString());
}
items.Add(new SelectListItem {
Selected = isSelected,
Text = displayName,
Value = item.ToString()
});
}
return items;
}
To support multiple languages, add translations for the display name keys, e.g. "Month_January", to the Resource file.
Now that the setup code has been abstracted away using some reflection magic, creating a new viewmodel is a breeze :>
public class PayNowViewModel {
// SelectListItems are only generated if this gets rendered
public Month ExpiryMonth { get; set; }
}
// Intial Action
var paymentGateway = new PayNowViewModel();
return View(paymentGateway);
// Razor View: call the EditorTemplate
#Html.EditorFor(m => m.ExpiryMonth)
Note that in the EditorTemplate, Model is passed as the selected item to ToSelectList. After postback, Model will hold the currently selected value. Therefore it stays selected, even if you just return the model after an error in the controller:
// HttpPost Action
if (!ModelState.IsValid) {
return View("MakePayment", paymentGatewayForm);
}
Took us some time to come up with this solution, credits go to the Saratiba team.
I am using MVC5, Razor, Entity Framework, C#. I am trying to pass a value of a dorpdown list using a link.
my model is
public class TestVM
{
public string TheID { get; set; }
}
I am loading an enum into a IEnumerable<SelectListItem>.
My enum is
public enum DiscountENUM
{
SaleCustomer,
SaleCustomerCategory,
SaleProduct,
SaleProductCategory,
SaleCustomerAndProduct,
SaleCustomerAndProductCategory,
SaleCustomerCategoryAndProductCategory,
PurchaseVendor,
PurchaseVendorAndProduct,
PurchaseVendorAndProductCategory,
PurchaseProduct,
PurchaseProductCategory,
Unknown
}
I am using the index method of the home controller
public ActionResult Index()
{
ViewBag.ListOfDiscounts = SelectListDiscountENUM();
TestVM d = new TestVM();
return View(d);
}
Where I load the ListOfDiscounts using:
private IEnumerable<SelectListItem> SelectListDiscountENUM()
{
List<SelectListItem> selectList = new List<SelectListItem>();
var listOfEnumValues = Enum.GetValues(typeof(DiscountENUM));
if (listOfEnumValues != null)
if (listOfEnumValues.Length > 0)
{
foreach (var item in listOfEnumValues)
{
SelectListItem sVM = new SelectListItem();
sVM.Value = item.ToString();
sVM.Text = Enum.GetName(typeof(DiscountENUM), item).ToString();
selectList.Add(sVM);
}
}
return selectList.OrderBy(x => x.Text).AsEnumerable();
}
My create method which is called from the view is
public ActionResult Create(TestVM d, string TheID)
{
return View();
}
My Index view is
#model ModelsClassLibrary.Models.DiscountNS.TestVM
<div>#Html.ActionLink("Create New", "Create", new { TheID = Model.TheID})</div>
<div>
#Html.DropDownListFor(x => x.TheID, #ViewBag.ListOfDiscounts as IEnumerable<SelectListItem>, "--- Select Discount Type ---", new { #class = "form-control" })
</div>
The problem is in the following line in the View
<div>#Html.ActionLink("Create New", "Create", new { TheID = Model.TheID })</div>
I have tried adding a model with the name of the field as "TheID"... no luck. Also, added a string field in the parameter, no luck. I looked at the FormControl object, and there was nothing in it either! I suspect something has to be added at the Route level in the helper, but I don't know what.
Model.TheID is always null. Even when I select an item in the DropDownListFor.
Does anyone have an idea how I can capture the select value of the DropDownListFor and send it into the Html.ActionLink TheID?
I am trying to populate a listbox from a model which is my Entity framework edmx model I seems to be populating but each character is appearing on a new line so for instance
H
A
L
O
instead of
HALO
this is my controller:
private dataEntities dbGame = new dataEntities();
[HttpGet]
public ActionResult Index()
{
dbdata dbdata = new dbdata();
List<SelectListItem> listSelectItems = new List<SelectListItem>();
foreach (dbdata dbdata in dbGame.dbdata)
{
SelectListItem selectList = new SelectListItem();
{
selectList.Text = dbdata.GameTitle;
selectList.Value = dbdata.GameId;
};
dbdata.GameTitle = selectList.Text;
dbdata.Value = selectList.Value;
listSelectItems.Add(selectList);
}
return View(dbdata);
}
razor:
#using (Html.BeginForm())
{
#Html.ListBox(Model.GameTitle, new SelectList(Model.GameTitle));
}
any suggestions as to why it's doing this?
it's because GameTitle property is type of string(a sequence of characters).
so, try like this:
ViewBag.items = listSelectItems;
return View(dbdata);
in your View:
#Html.ListBox(Model.GameTitle,(IEnumerable<SelectListItems>)ViewBag.items)
you don't need:
dbdata.GameTitle = selectList.Text;
dbdata.Value = selectList.Value;
Or you can put a property of type List<SelectListItem> in your dbdata model, then assign listSelectItems to it then return View(dbdata);.
I know there are many custom implementation of CheckBoxListFor helper method to fill up the missing feature in the MVC framework. But I am not ready to use them just yet. I am interested in creating a checkbox list using MVC 4 or 5 provided features only. So, I created this model class:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace MvcApplication1.Models
{
public class FruitViewModel
{
public int[] SelectedFruits { get; set; }
public IList Fruits
{
get
{
return new List{
new Fruit{Id=1, Name="Apple", Selected = false},
new Fruit{Id=2, Name="Banana", Selected = false},
new Fruit{Id=3, Name="Cherry", Selected = false},
new Fruit{Id=4, Name="Durian", Selected = false},
new Fruit{Id=5, Name="Elderweiss Grape", Selected = false}
};
}
}
}
public class Fruit
{
public int Id { get; set; }
public string Name { get; set; }
public bool Selected { get; set; }
}
}
And here is the controller class:
using MvcApplication1.Models;
using System.Web.Mvc;
namespace MvcApplication1.Controllers
{
public class FruitController : Controller
{
public ActionResult Index()
{
FruitViewModel model = new FruitViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(FruitViewModel model)
{
if (ModelState.IsValid)
{
}
return View(model);
}
}
}
And here is the razor view:
#model MvcApplication1.Models.FruitViewModel
#{
ViewBag.Title = "Index";
}
<h2>Select your favorite fruits</h2>
#using (Html.BeginForm("Index", "Fruit"))
{
<p>Using Html helper method:</p>
for (int i = 0; i < Model.Fruits.Count; i++)
{
#Html.CheckBoxFor(m => m.Fruits[i].Selected) #Model.Fruits[i].Name<br />
}
<p>Plain html without html helper</p>
for (int i = 0; i < Model.Fruits.Count; i++)
{
<input type="checkbox" name="SelectedFruits" value="#Model.Fruits[i].Id" checked="#Model.Fruits[i].Selected" />
#Model.Fruits[i].Name<br />
}
<input type="submit" name="submit" value="Submit" />
}
Here are the problems I am having:
The plain HTML version will populate the SelectedFruits collection with my selected fruits' Ids properly, as you can see from the screen shot. But when the page refreshes after post back, the selected checkboxes status is reset to not checked.
The version using the Html helper CheckBoxFor will not populate my SelectedFruits collection with my selected fruits' IDs, although it does seem to maintain the check box status as checked after post back completes.
So, in either case, there is a big problem.
What is the correct way to set up a checkbox list such that I can get the SelectedFruits collection populated correctly and the status of the checkboxes maintained after form post completes (important when I add other stuff on the page and if validation fails).
I would suggest that you can use the following solution,
Controller Change [Only the Get Method to set the data for the view to display]
public ActionResult Index()
{
FruitViewModel model = new FruitViewModel();
model.Fruits = new List<Fruit>
{
new Fruit{Id=1, Name="Apple", Selected = false},
new Fruit{Id=2, Name="Banana", Selected = false},
new Fruit{Id=3, Name="Cherry", Selected = false},
new Fruit{Id=4, Name="Durian", Selected = false},
new Fruit{Id=5, Name="Elderweiss Grape", Selected = false}
};
return View(model);
}
FruitModel Change, we are setting the property to be populated dynamically based on the fruits that the user has selected.
public int[] SelectedFruits
{
get
{
return Fruits != null && Fruits.Count > 0
? Fruits.Where(f => f.Selected == true).Select(f => f.Id).ToArray()
: new int[0];
}
set { }
}
View Page Change [Index.cshtml]
for (int i = 0; i < Model.Fruits.Count; i++)
{
#Html.CheckBoxFor(m => m.Fruits[i].Selected) #Model.Fruits[i].Name<br />
#Html.HiddenFor(m => m.Fruits[i].Id)
#Html.HiddenFor(m => m.Fruits[i].Name)
}
The problem that I found in your code was that you have used the checkbox name property as "SelectedFruits" in the html for the checkbox that you have manually rendered. Whereas the markup rendered by the Html Helper is having the name of "Fruits[0].Selected" etc...
Hence, the selected fruits was not properly modelbound.
Kindly verify the currently generated markup and post your feedback in case of any queries.
Your problem is that the Fruits property in your viewmodel always creates a new array where the Selected property for each fruit is always false. You can fix it by just initializing the array once in your viewmodel instead:
public IList<Fruit> Fruits
{
get
{
if (_fruits == null)
_fruits = new List<Fruit> {
new Fruit{Id=1, Name="Apple", Selected = false},
new Fruit{Id=2, Name="Banana", Selected = false},
new Fruit{Id=3, Name="Cherry", Selected = false},
new Fruit{Id=4, Name="Durian", Selected = false},
new Fruit{Id=5, Name="Elderweiss Grape", Selected = false}
}
return _fruits;
};
}
private List<Fruit> _fruits;
Once that's fixed, you'll have to update your controller to fix the Selected properties:
[HttpPost]
public ActionResult Index(FruitViewModel model)
{
foreach (int fruitId in model.SelectedFruits)
model.Fruits[fruitId].Selected = true;
return View(model);
}