I've tried to follow a few examples from here and a couple other resources to just create a very simple member in my viewmodel and display it as a dropdown list on my view with a dropdownlistfor() helper. I can't seem to wrap my head around it and it's not working with what I'm trying.
here's my viewmodel:
public class Car
{
public int CarId { get; set; }
public string Name { get; set; }
}
public class MyViewModel
{
public IEnumerable<Car> Cars = new List<Car> {
new Car {
CarId = 1,
Name = "Volvo"
},
new Car {
CarId = 2,
Name = "Subaru"
}
};
public int MyCarId { get; set; }
}
and here is my view:
#Html.DropDownListFor(m => m.MyCarId, new SelectList(Model.Cars, "CarId", "Name"))
and here is my controller:
public ActionResult MyView()
{
return View();
}
You need to make sure you send the Model to your View:
public ActionResult Index()
{
var myViewModel = new MyViewModel()
return View(myViewModel);
}
And in your View you need to make sure you're defining your Model:
#model namespace.MyViewModel
You example works fine, I think you forgot to send MyViewModel in POST Action.
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
Related
I am just learning MVC. Here's What I have tried so far:
public class StoreXml
{
public string StoreCode { get; set; }
public static IQueryable<StoreXml> GetStores()
{
return new List<StoreXml>
{
new StoreXml { StoreCode = "0991"},
new StoreXml { StoreCode = "0015"},
new StoreXml { StoreCode = "0018"}
}.AsQueryable();
}
In Controller:
public SelectList GetStoreSelectList()
{
var Store = StoreXml.GetStores();
return new SelectList(Store.ToArray(),"StoreCode");
}
public ActionResult IndexDDL()
{
ViewBag.Store = GetStoreSelectList();
return View();
}
In View:
#Html.DropDownList("store", ViewBag.Stores as SelectList, "Select a Store")
What am I doing wrong here? Dropdown shows Cie_Mvc.Models.StoreXml only but no values. Please suggest.
You're storing it in ViewBag.Store and calling it in the View, ViewBag.Stores
public ActionResult IndexDDL()
{
ViewBag.Stores = GetStoreSelectList();
return View();
}
#Html.DropDownList("store", ViewBag.Stores as SelectList, "Select a Store")
As a side note, this is the issue with using a dynamic object. I would suggest putting the property in a ViewModel, so you get intellisense.
I would do it differently. I would separate my class from my list of the classes like:
public class StoreXml
{
public string StoreCode { get; set; }
}
Then I would use something like a repository to get some data, even if it is hard coded, or you can just populate a list from your controller. Always use a view model to represent your data on the view:
public class MyViewModel
{
public string StoreXmlCode { get; set; }
public IEnumerable<StoreXml> Stores { get; set; }
}
And then your controller can look something like this:
public class MyController
{
public ActionResult MyActionMethod()
{
MyViewModel viewModel = new MyViewModel();
viewModel.Stores = GetStores();
return View(viewModel);
}
private List<StoreXml> GetStores()
{
List<StoreXml> stores = new List<StoreXml>();
stores.Add(new StoreXml { StoreCode = "0991"});
stores.Add(new StoreXml { StoreCode = "0015"});
stores.Add(new StoreXml { StoreCode = "0018"});
return stores;
}
}
And then your view could look something like this:
#model MyProject.ViewModels.Stores.MyViewModel
#Html.DropDownListFor(
x => x.StoreXmlCode,
new SelectList(Model.Stores, "StoreCode", "StoreCode", Model.StoreXmlCode),
"-- Select --"
)
I hope this can lead you in the right direction :)
I am trying to update a compound page model which as one of its properties has a list of objects.
My Model looks like this:
public class PageViewModel
{
public ProgramListVM ProgramsDDL { get; set; }
public PageViewModel()
{
this.ProgramsDDL = new ProgramListVM();
}
}
The ProgramListVM class is:
public class ProgramListVM
{
public List<ProgramVM> Program_List { get; set; }
public int SelectedValue { get; set; }
public ProgramListVM()
{
this.Program_List = new List<ProgramVM>();
this.SelectedValue = 0;
}
}
and ProgramVM is:
public class ProgramVM
{
public int ProgramID { get; set; }
public string ProgramDesc { get; set; }
public ProgramVM(int id, string code)
{
this.ProgramID = id;
this.ProgramDesc = code;
}
}
I try to render this dropdownlist by the following two:
1-
<%: Html.DropDownList("ProgramsDDL", new SelectList(Model.Page6VM.ProgramsDDL.Program_List, "ProgramID", "ProgramDesc", Model.Page6VM.ProgramsDDL.SelectedValue))%>
2-
<%: Html.DropDownListFor(m => m.Page6VM.ProgramsDDL.Program_List, new SelectList(Model.Page6VM.ProgramsDDL.Program_List, "ProgramID", "ProgramDesc"), Model.Page6VM.ProgramsDDL.SelectedValue)%>
But when I try to update my model through a controller action
[HttpPost]
public ActionResult UpdateUser(PageViewModel model)
{
}
model.ProgramsDDL.count is zero.
What is the best way to render this dropdownlist and be able to set the selected index, and also be able to send the selected index back to the controller?
You mixed up the parameters for Html.DropDownListFor(). Code sample below should work.
<%: Html.DropDownListFor(m => m.SelectedValue,
new SelectList(Model.Page6VM.ProgramsDDL.Program_List, "ProgramID", "ProgramDesc"),
null) %>
You also should have a SelectedValue in your model that's posted back.
public class PageViewModel
{
public ProgramListVM ProgramsDDL { get; set; }
public int SelectedValue { get; set; }
public PageViewModel()
{
this.ProgramsDDL = new ProgramListVM();
}
}
Also default model binder can't map complex collections to your model. You probably don't need them in your post action anyway.
Hi im using RenderAction helper and during rendering i have this error:
Error executing child request for
handler
'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'.
Controller code:
public ActionResult GetSearcher()
{
SearcherLines sl = new SearcherLines()
{
RegionList = db.Region.ToList(),
CategoryList = db.Category.ToList(),
SearchValue = null
};
return PartialView(sl);
}
ViewModel
public class SearcherLines
{
public List<Region> RegionList { get; set; }
public List<Category> CategoryList { get; set; }
public string SearchValue { get; set; }
}
partialView
#model IEnumerable<MasterProject.JobForYou.v3.Models.ViewModels.SearcherLines>
#foreach (var i in Model)
{
<p>#i.CategoryList</p><br />
}
Please help.
Try setting OutputCache attribute on controller action
I am trying to create a dropdown list to display all the value in a custom collection class
such as
public class MyCustomClassCollection
{
public List<MyCustomClass> {get;set;}
}
I want it to show the Description:string of each MyCustomClass
I tried
<%: Html.DropDownList "Description", MyCustomClass %>
Resharper suggests that I cast MyCustomClass to IEnemerable
but the server returns an unable to cast error.
Any Idea how I can create this DropDownList?
__Modification___
public class ViewModel
{
public Detail detail { get; set; }
}
public class Detail //Inherited from webservce
{
public CustomClassCollection {get;set;}
.... Other Properties, a.k.a Custom Classes
}
public class CustomClassCollection
{
public List<CustomClass> {get;set;}
}
public class CustomClass {
public int Id {get;set;}
public string Description{get;set;}
... other properties
}
public ActionResult Index(int? id, DateTime? date)
{
if (id.Equals(null))
id = ######### ;
if (date.Equals(null))
date = DateTime.Today;
var vm = new ViewModel
{
Detail = _repository.Detail((int)id,(DateTime)date)
};
return View(vm);
}
The second argument of the DropDownList helper must be an IEnumerable<SelectListItem> or a SelectList which implements this interface for that matter. So in your controller action organize in such a way that you convert your custom collection into an IEnumerable<SelectListItem>. As always you could start by writing a view model:
public class MyViewModel
{
public string SelectedDescription { get; set; }
public SelectList Descriptions { get; set; }
}
and then have your controller action query the custom list and populate the view model which will be passed to the view:
public ActionResult Index()
{
var descriptions = yourCustomCollection.MyCustomClass.Select(x => new
{
Value = x.Description,
Text = x.Description
});
var model = new MyViewModel
{
Descriptions = new SelectList(descriptions, "Value", "Text")
};
return View(model);
}
and finally in your strongly typed view:
<%= Html.DropDownListFor(x => x.SelectedDescription, Model.Decriptions) %>
UPDATE:
After posting your updated models (which by the way are still incomplete and impossible to compile as you haven't provided any property names), here's an example:
public class ViewModel
{
public int SelectedId { get; set; }
public Detail Detail { get; set; }
}
public class Detail
{
public CustomClassCollection MyCollection { get; set; }
}
public class CustomClassCollection
{
public List<CustomClass> CustomClass { get; set; }
}
public class CustomClass
{
public int Id { get; set; }
public string Description { get; set; }
}
public class HomeController : Controller
{
public ActionResult Index()
{
var vm = new ViewModel
{
Detail = new Detail
{
MyCollection = new CustomClassCollection
{
CustomClass = new List<CustomClass>
{
new CustomClass
{
Id = 1,
Description = "description 1",
},
new CustomClass
{
Id = 2,
Description = "description 2",
},
new CustomClass
{
Id = 3,
Description = "description 3",
},
}
}
}
};
return View(vm);
}
}
and in the view:
<%= Html.DropDownListFor(
x => x.SelectedId,
new SelectList(Model.Detail.MyCollection.CustomClass, "Id", "Description")
) %>
What you have to understand in order to define a dropdown list in ASP.NET MVC ius that you need 2 things:
A scalar property to bind the selected value to (SelectedId in my example)
A collection to bind the list to (Model.Detail.MyCollection.CustomClass in the example)
I have a model like so:
return new MyViewModel()
{
Name = "My View Model",
Modules = new IRequireConfig[]
{
new FundraisingModule()
{
Name = "Fundraising Module",
GeneralMessage = "Thanks for fundraising"
},
new DonationModule()
{
Name = "Donation Module",
MinDonationAmount = 50
}
}
};
The IRequireConfig interface exposes a DataEditor string property that the view uses to pass to #Html.EditorFor like so:
#foreach (var module in Model.Modules)
{
<div>
#Html.EditorFor(i => module, #module.DataEditor, #module.DataEditor) //the second #module.DataEditor is used to prefix the editor fields
</div>
}
When I post this back to my controller TryUpdateModel leaves the Modules property null. Which is pretty much expected since I wouldnt expect it to know which concrete class to deserialize to.
Since I have the original model still available when the post comes in I can loop over the Modules and get their Type using .GetType(). It seems like at this point I have enough information to have TryUpdateModel try to deserialize the model, but the problem is that it uses a generic type inference to drive the deserializer so it does not actually update any of the properties except the ones defined in the interface.
How can I get update my Modules array with their new values?
If any particular point isnt clear please let me know and I will try to clarify
You could use a custom model binder. Assuming you have the following models:
public interface IRequireConfig
{
string Name { get; set; }
}
public class FundraisingModule : IRequireConfig
{
public string Name { get; set; }
public string GeneralMessage { get; set; }
}
public class DonationModule : IRequireConfig
{
public string Name { get; set; }
public decimal MinDonationAmount { get; set; }
}
public class MyViewModel
{
public string Name { get; set; }
public IRequireConfig[] Modules { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Name = "My View Model",
Modules = new IRequireConfig[]
{
new FundraisingModule()
{
Name = "Fundraising Module",
GeneralMessage = "Thanks for fundraising"
},
new DonationModule()
{
Name = "Donation Module",
MinDonationAmount = 50
}
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
View:
#model MyViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Name)
for (int i = 0; i < Model.Modules.Length; i++)
{
#Html.Hidden("Modules[" + i + "].Type", Model.Modules[i].GetType())
#Html.EditorFor(x => x.Modules[i])
}
<input type="submit" value="OK" />
}
and finally the custom model binder:
public class RequireConfigModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeParam = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
if (typeParam == null)
{
throw new Exception("Concrete type not specified");
}
var concreteType = Type.GetType(typeParam.AttemptedValue, true);
var concreteInstance = Activator.CreateInstance(concreteType);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => concreteInstance, concreteType);
return concreteInstance;
}
}
which you would register in Application_Start:
ModelBinders.Binders.Add(typeof(IRequireConfig), new RequireConfigModelBinder());
Now when the form is submitted the Type will be sent and the model binder will be able to instantiate the proper implementation.