Alright, I have a jQuery version of Chosen applied to a select displaying properly on my page and I've done so with the following code. First I have a BaseController that sets a ViewBag property listing all of the possible categories:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
try
{
_connection.Open();
this.ViewBag.AvailableCategories = new MultiSelectList(_connection.Query<Category>("select * from Category"), "CategoryID", "Name");
}
catch (Exception ex)
{
throw new HttpException(500, ex.Message);
}
finally
{
_connection.Close();
}
base.OnActionExecuted(filterContext);
}
Next, when navigating to /Event/View/1 I have the EventController setup (NOTE this controller bases the aforementioned controller) with the following View method.
public ActionResult View(int id)
{
Event evt = null;
try
{
evt = Event.Where(_connection, id);
if (evt == null)
{
throw new HttpException(404, "Oops! The event you're looking for does not exist.");
}
}
catch (Exception ex)
{
throw new HttpException(500, ex.Message);
}
return View(evt);
}
It sets the model, as you can see, to the following model.
public class Event
{
public int EventID { get; set; }
public int BusinessID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int NeighborhoodID { get; set; }
public IEnumerable<int> CategoryIds
{
get
{
if (Categories == null) { return new List<int>(); }
return Categories.Select(c => c.CategoryID).AsEnumerable();
}
}
public List<EventCategory> Categories { get; set; }
public List<EventHours> Hours { get; set; }
public static Event Where(IDbConnection connection, int id)
{
Event result = null;
try
{
connection.Open();
var sql =
#" select * from Event where EventID = #EventID
select * from EventCategory where EventID = #EventID
select * from EventHours where EventID = #EventID";
using (var multiResult = connection.QueryMultiple(sql, new { EventID = id }))
{
result = multiResult.Read<Event>().FirstOrDefault();
if (result != null)
{
result.Categories = multiResult.Read<EventCategory>().ToList();
result.Hours = multiResult.Read<EventHours>().ToList();
}
}
}
finally
{
connection.Close();
}
return result;
}
}
Finally I have the following markup in the view.
#Html.DropDownListFor(m => m.CategoryIds,
ViewBag.AvailableCategories as MultiSelectList,
new { multiple = "multiple", #class = "chzn-container" })
Now, as I stated at first, it's displaying properly and listing all of the categories as expected. However, even though the CategoryIds property does list in fact two selected categories, it's not setting them (or showing them for that matter) as selected in the Chosen drop down on load.
I am forced to only further assume that if a user selected a value from the Chosen drop down that it would not bind properly on post either because it does not change the HTML when an item is selected.
So, my question is this, how do I properly two-way data bind to a Chosen drop down in MVC?
ListBoxFor is the helper you should use for multiselect lists
replacing
#Html.DropDownListFor(m => m.CategoryIds,
ViewBag.AvailableCategories as MultiSelectList,
new { multiple = "multiple", #class = "chzn-container" })
with
#Html.ListBoxFor(m => m.CategoryIds,
ViewBag.AvailableCategories as MultiSelectList,
new { multiple = "multiple", #class = "chzn-container" })
should do the trick
Related
Background
I have master-detail gridview MVC 5 project using EF 6 Database First & DevExpress 15.2. The project structure given below (feel free to reproduce):
Model (ProjectModel.cs)
namespace Project.Models
{
// EF data context
public class ProjectContext : DbContext
{
public ProjectContext : base("DatabaseConnection")
{
}
public DbSet<Receipt> Receipts { get; set; }
public DbSet<SoldList> SoldLists { get; set; }
}
[Table("Receipt")]
public class Receipt
{
[Key]
public int ReceiptID { get; set; }
public String SessionID { get; set; }
public String MemberID { get; set; }
public String MemberName { get; set; }
public DateTime ReceiptDate { get; set; }
public String TransID { get; set; }
public decimal TotalPrice { get; set; }
public decimal RemainingCredit { get; set; }
public bool? UseRemainingCredit { get; set; }
}
[Table("SoldList")]
public class SoldList
{
[Key]
public int ItemID { get; set; }
public String SessionID { get; set; }
public String MemberID { get; set; }
public int TransID { get; set; }
public decimal TotalPrice{ get; set; }
public bool? UseRemainingCredit { get; set; }
}
}
Controller (MasterController.cs)
namespace Project.Controllers
{
public class MasterController : Controller
{
public ViewResult Transaction()
{
using (var DB = new ProjectContext())
{
var model = (from record in DB.Receipts select record).ToList();
return View(model);
}
}
// master grid partial view
public PartialViewResult PVTransaction()
{
using (var DB = new ProjectContext())
{
var model = (from record in DB.Receipts select record).ToList();
return PartialView("_MasterTrans", model);
}
}
// detail grid partial view
public PartialViewResult PVDetails(String sessionID, String memberID, String transID)
{
ViewData["TransID"] = transID;
// edit: adding forgotten IDisposable context declaration
using (var DB = new ProjectContext())
{
var model = (from list in DB.SoldLists where list.TransID == transID && list.MemberID == memberID && list.SessionID == sessionID select list).ToList();
return PartialView("_DetailTrans", model);
}
}
[HttpPost]
[ValidateInput(false)]
public PartialViewResult EditTrans(Receipt item)
{
using (var DB = new ProjectContext())
{
var model = (from record in DB.Receipts select record).ToList();
try
{
var match = DB.Receipts.FirstOrDefault(x => x.ReceiptID == item.ReceiptID);
if (match != null)
{
var sum = DB.SoldLists.Where(x => x.TransID == match.TransID).Select(x => x.TotalPrice).Sum();
var useRemaining = DB.SoldLists.Where(x => x.TransID == match.TransID).Select(x => x.UseRemainingCredit).FirstOrDefault();
if (item.TotalPrice - match.RemainingCredit != sum && (useRemaining == null || useRemaining == false))
{
// other unrelated code
}
else
{
match.SessionID = item.SessionID;
match.MemberID = item.MemberID;
// other data update definitions
this.UpdateModel(match);
DB.SaveChanges();
ViewBag.Success = "Selected data edited successfully.";
}
}
}
catch (Exception e)
{
ViewBag.Error = "Problem on editing with error message: \'" + e.Message + "\'";
}
return PartialView("_MasterTrans", model);
}
}
}
View (Transaction.cshtml)
#using Project.Models
<script type="text/javascript">
function onMasterInit(s, e) {
MasterGrid.PerformCallback();
}
function endMasterCallback(s, e) {
if (s.cpMasterMsg) {
alert(s.cpMasterMsg);
delete s.cpMasterMsg;
MasterGrid.Refresh();
}
else if (s.cpErrorMsg) {
alert(s.cpErrorMsg);
delete s.cpErrorMsg;
MasterGrid.Refresh();
}
}
</script>
<label class="description">Transaction List</label>
#using (Html.BeginForm())
{
Html.RenderAction("PVTransaction", "Master");
}
Partial View (_MasterTrans.cshtml)
#using Project.Models
#{
GridViewExtension grid = Html.DevExpress().GridView(settings =>
{
settings.Name = "MasterGrid";
settings.CallbackRouteValues = new { Controller = "Master", Action = "PVTransaction" };
settings.Width = Unit.Percentage(100);
settings.CommandColumn.ShowEditButton = true;
settings.CommandColumn.ShowUpdateButton = true;
settings.CommandColumn.ShowCancelButton = true;
settings.SettingsBehavior.AllowFocusedRow = true;
settings.SettingsBehavior.AllowSelectByRowClick = true;
settings.SettingsBehavior.ConfirmDelete = true;
settings.SettingsContextMenu.Enabled = true;
settings.SettingsContextMenu.EnableRowMenu = DefaultBoolean.True;
settings.SettingsContextMenu.EnableColumnMenu = DefaultBoolean.True;
settings.SettingsEditing.Mode = GridViewEditingMode.PopupEditForm;
settings.SettingsEditing.UpdateRowRouteValues = new { Controller = "Master", Action = "EditTrans" };
settings.SettingsText.ConfirmDelete = "Remove selected data?";
settings.SettingsText.ContextMenuExpandDetailRow = "Show Details";
settings.SettingsText.ContextMenuCollapseDetailRow = "Hide Details";
settings.ClientSideEvents.Init = "onMasterInit";
settings.ClientSideEvents.EndCallback = "endMasterCallback";
settings.CustomJSProperties = (s, e) =>
{
if (ViewBag.Success != null)
{
e.Properties["cpMasterMsg"] = ViewBag.Success.ToString();
e.Properties["cpErrorMsg"] = ViewBag.Error.ToString();
}
};
// begin detail gridview
settings.SettingsDetail.AllowOnlyOneMasterRowExpanded = false;
settings.SettingsDetail.ShowDetailButtons = true;
settings.SettingsDetail.ShowDetailRow = true;
settings.SetDetailRowTemplateContent(content =>
{
ViewContext.Writer.Write(#"<p style=""font-size:14px; text-decoration:underline"">Transaction Detail of " + DataBinder.Eval(content.DataItem, "MemberName") + " at " + String.Format("{0:yyyy/MMMM/dd}", DataBinder.Eval(content.DataItem, "ReceiptDate")) + ", Session ID: " + DataBinder.Eval(content.DataItem, "SessionID") + "</p>");
// this line throws FormatException on editing master grid while detail grid still open
Html.RenderAction("PVDetails", "Master", new { sessionID = DataBinder.Eval(content.DataItem, "SessionID"), memberID = DataBinder.Eval(content.DataItem, "MemberID"), transID = DataBinder.Eval(content.DataItem, "TransID")});
});
// end of detail gridview
settings.KeyFieldName = "ReceiptID";
settings.Columns.Add(column =>
{
column.FieldName = "ReceiptID";
column.Caption = "Receipt ID";
}
// other columns with similar construction
// including SessionID, MemberID, MemberName, ReceiptDate, TransID columns
}
}
#grid.Bind(Model).GetHtml()
Partial View (_DetailTrans.cshtml)
#using Project.Models
#{
GridViewExtension grid = Html.DevExpress().GridView(settings =>
{
settings.Name = "DetailGrid" + ViewData["TransID"]; // using transaction ID column from master grid to generate unique detail grid name
settings.CallbackRouteValues = new { Controller = "Master", Action = "PVDetails" };
settings.SettingsDetail.MasterGridName = "MasterGrid";
settings.KeyFieldName = "ItemID";
settings.Columns.Add(column =>
{
column.Name = "ItemID";
column.Caption = "Item ID";
});
// other columns with similar construction
}
}
#grid.Bind(Model).GetHtml()
Problem Statement
Master gridview provides editing transaction data and detail gridview shows item list by values from master grid. When user clicking Edit button, a popup edit window shows up providing editable data.
TransID property contains random 40-digit hexadecimal ensuring valid transactions.
All edit operations work fine if detail grid was hidden, but if a user trying to edit & update master grid content when a detail gridview still open (with collapse sign), FormatException was thrown (see details).
Despite view throwing exception, the edited data saved successfully but it will redirect to custom error page. I searched by keyword given inside additional information with no results.
Exception Details
An exception of type 'System.FormatException' occurred in System.Web.dll was caught.
HResult: -2146233033
Source: DevExpress.Web.v15.2
Additional information: Unexpected 'O' at 0
Stack Trace (simplified):
at DevExpress.Web.Internal.JsonReader.ReadCore()
at DevExpress.Web.Internal.JsonReader.Read(String jsonString)
at DevExpress.Web.Internal.HtmlConvertor.FromJSON(String jsonString)
at DevExpress.Web.Mvc.Internal.ExtensionValueProvidersFactory.GetValueProvider(ModelBindingContext bindingContext, String propertyName)
at DevExpress.Web.Mvc.DevExpressEditorsBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor)
at System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
at DevExpress.Web.Mvc.Internal.ContentControl`1.RenderInternal(HtmlTextWriter writer)
at DevExpress.Web.Mvc.Internal.ContentControl`1.Render(HtmlTextWriter writer)
at DevExpress.Web.Mvc.ExtensionBase.RenderControl(Control control)
at DevExpress.Web.Mvc.ExtensionBase.RenderCallbackResultControl()
at DevExpress.Web.Mvc.GridViewExtension.RenderCallbackResultControl()
at DevExpress.Web.Mvc.ExtensionBase.RenderCallbackResult()
Expected Result(s)
I want all editing operations running fine for every detail grid states.
Any help and suggestions welcome, thanks in advance.
I've tried to built a Kendo Treeview linked to a List in C# and ASP.NET MVC, but each time I run the Application it displays instead of the List I get a Request Failed error.
My C# code
The Controller:
public JsonResult RetrieveData()
{
var testList = new List<AssetsVM>();
var root = new AssetsVM { id = 1 , name = "Elsman" };
testList.Add(root);
root.Children.Add(new AssetsVM { id = 2, name = "Elsman1" });
root.Children.Add(new AssetsVM { id = 3, name = "Elsman2" });
return Json(testList, JsonRequestBehavior.AllowGet);
}
the List code
The View Model
public class AssetsVM
{
public string name { get; set; }
public long id { get; set; }
public List<AssetsVM> Children { get; set; }
public AssetsVM()
{
this.Children = new List<AssetsVM>();
}
public AssetsVM(tbl_Assets x)
{
this.name = x.Name;
this.id = x.ID;
this.Children = new List<AssetsVM>();
}
public bool HasChildren
{
get { return Children.Any(); }
}
}
The HTML/Kendo Code
The View:
<div class="kendo-tree">
#(Html.Kendo().TreeView()
.Name("treeViewDatabase")
.DataTextField("name")
.DataSource(dataSource => dataSource
.Model(m => m
.Id("id")
.HasChildren("HasChildren")
)
.Read(read => read.Action("RetrieveData", "BeheerController"))
)
//.Events(e => e.Select("Index"))
.HtmlAttributes(new { style = "color:white;"})
)
</div>
Does anyone see the problem why I get a "Request Failed" Error?
Thanks in Advance
PS. The Table is not in use during the code but its already there for preporations
I am a beginner in ASP MVC, and, after a lot of help from SO, am progressing through ViewModels. Using a ViewModel however, I have encountered the following error.
Given the following View:
#model November.ViewModels.Staff_Salutation_VM
//...
using (Html.BeginForm("UpdateStaff", "Settings", FormMethod.Post,
new { #class = "clearfix parameter-form update-parameter update-staff", enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
//...
#for (int i = 0; i < Model.AvailableStaffMembers.Count; i++)
{
var staff = Model.AvailableStaffMembers[i];
<tr>
<td>#Html.HiddenFor(model => staff.ID)#Html.ValueFor(model => staff.ID)</td>
<td>
#Html.DropDownListFor(
model => model.SalutationID, Model.AvailableSalutations.Select(option => new SelectListItem
{
Text = option.Desc.ToString(),
Value = option.ID.ToString(),
Selected = (option.ID.ToString() == staff.SalutationID.ToString())
}
),
"Choose...")
</td>
<td>#Html.EditorFor(model => staff.FName)</td>
<td>#Html.EditorFor(model => staff.LName)</td>
<td>#Html.EditorFor(model => staff.Active)</td>
<td>Delete</td>
</tr>
}
and the following Controller:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using November.Models;
using November.ViewModels;
using November.DAL;
//...
//GET
var staffCreateViewModel = new Staff_Salutation_VM();
staffCreateViewModel.AvailableSalutations = new List<Prm_Salutation>();
var activeSalts = (from a in db.Prm_Salutations
where a.Active == true
orderby a.Desc ascending
select a);
staffCreateViewModel.AvailableSalutations = activeSalts.ToList();
staffCreateViewModel.AvailableStaffMembers = new List<Prm_Staff>();
var activeStaff = (from a in db.Prm_Staffs
where a.Active == true
orderby a.LName ascending
select a);
staffCreateViewModel.AvailableStaffMembers = activeStaff.ToList();
return View("StaffMembers", staffCreateViewModel);
//POST
public ActionResult UpdateStaff(Staff_Salutation_VM list)
{
if (ModelState.IsValid)
{
foreach (var formData in list) //no longer works due to dropping List<>
{
var tbl = db.Prm_Staffs.Where(a => a.ID.Equals(formData.ID)).FirstOrDefault();
if (tbl != null)
{
var Prm_StaffModel = new Prm_Staff();
Prm_StaffModel.SalutationID = formData.SalutationID;
Prm_StaffModel.FName = formData.FName;
Prm_StaffModel.LName = formData.LName;
Prm_StaffModel.Active = formData.Active;
}
}
db.SaveChanges();
ViewBag.UpdateRtrn = "Successfully Updated.";
return RedirectToAction("Parameters", new { param = "Staff Members" });
}
else
{
ViewBag.UpdateRtrn = "Failed ! Please try again.";
return RedirectToAction("Parameters", new { param = "Staff Members" });
}
}
return RedirectToAction("Parameters", new { param = "Staff Members" });
}
And, for good measure, the ViewModel itself:
public class Staff_Salutation_VM
{
public int ID { get; set; }
public int SalutationID { get; set; }
public string FName { get; set; }
public string LName { get; set; }
public bool Active { get; set; }
public List<Prm_Salutation> AvailableSalutations { get; set; }
public List<Prm_Staff> AvailableStaffMembers { get; set; }
public Staff_Salutation_VM() { }
}
When triggered, no form values populate the ActionResult, resulting in a Object reference not set to an instance of an object. exception being thrown when the foreach (var formData in list) line is reached. Debugging shows list as being null. How can this be so? Or rather, what am I doing wrong?
EDIT: the list variable in my POST ActionResult is now getting data - or at least, is showing the various types in the class when debugged. How do I then iterate through it to save that data in the appropriate rows of the DB?
I totally missed the method signature, sorry! Your initial view load passes a model Staff_Salutation_VM but your UpdateStaff (form posted) is expecting List<Staff_Salutation_VM>. These are different animals. Change public ActionResult UpdateStaff(List<Staff_Salutation_VM> list) to public ActionResult UpdateStaff(Staff_Salutation_VM staff) just to see if you get past the null ref exception. Note, you'll need to remove your foreach since you don't have an IEnumerable coming in.
I hope this post will be helpful for you.
Model Binding To A List
I have a table in my database which I select all Mem_NA to fill it in my dropdownlist in my view.
I can't understand how can I fill the values in there? Someone can give me a hand?
My Model
public class MemberBasicData
{
public int Id { get; set; }
public string Mem_NA { get; set; }
public string Mem_Occ { get; set; }
}
public List<MemberBasicData> GetAllMembers()
{
DateTime today = DateTime.Now;
List<MemberBasicData> mbd = new List<MemberBasicData>();
using (SqlConnection con = new SqlConnection(Config.ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("SELECT Id,Mem_NA,Mem_Occ FROM Mem_Basic", con))
{
try
{
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
MemberBasicData mb = new MemberBasicData();
mb.Id = (int)reader["Id"];
mb.Mem_NA = (string)reader["Mem_NA"];
mb.Mem_Occ =(string)reader["Mem_Occ"];
mbd.Add(mb);
}
}
catch (Exception e)
{
throw e;
}
finally
{
if (con.State == System.Data.ConnectionState.Open)
{
con.Close();
}
}
return mbd;
}
}
}
GetAllMembers() function is an IEnumerable function which I was used for get all details of members. Can I use the same function for filling the dropdown list?
My controller
public ActionResult Register()
{
return View(new Member());
}
And View
#Html.DropDownListFor("-- Select --", new SelectList("")) <=== ???? i dont know
Model
public MemberModel
{
List<SelectListItem> MemberList { get; set; }
}
Controller
public ActionResult Register()
{
MemberModel model = new MemberModel();
model.MemberList = GetAllMembers()
.Select(m => new SelectListItem
{
Text = string.format("{0}, {1}", m.Mem_NA, m.Mem_Occ),
Value = m.Id
});
SelectListItem default = new SelectListItem
{
Text = "-- SELECT --",
Value = 0
};
model.MemberList.Insert(0, default);
return View(model);
}
View
#Html.DropDownList(Model.MemberList);
UPDATE: This version adds a default entry to the beginning of the list.
#Html.DropDownListFor gets an IEnumerable of SelectListItem. But instead, you want to use a list of MemberBasicData. You must convert your MemberBasicData list to SelectListItem and pass it to the #Html.DropDownListFor instead of a list of MemberBasicData. For passing related data to the Views as Sirwan pointed, you can use ViewBag or ViewData.
Having a hard time getting this dropDownList to Bind. This is simply a list of states. The Model.State is "TX", but my dropdownlist is showing "AK", which is the first value in the list. Any thoughts?
<%: Html.DropDownListFor(
model => model.State,
new SelectList(
muniSynd.getStatecodesDropDown(),
"Value",
"Text",
Model.State.ToString().Trim()
)
)
%>
In my muniSynd class, which is a wrapper of my dataContext.....
public IList<StateCodeViewModel> getStatecodesDropDown()
{
var states = from p in this._MuniDc.Statecodes
select new StateCodeViewModel
{
Value = p.Statecode1.ToString().Trim(),
Text = p.Statecode1.ToString().Trim()
};
return states.ToList();
}
public class StateCodeViewModel
{
public string Value{get;set;}
public string Text{get;set;}
}
Im using LinqToSQL, here is the object
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Statecodes")]
public partial class Statecode
{
private string _Statecode1;
public Statecode()
{
}
[global::System.Data.Linq.Mapping.ColumnAttribute(Name="Statecode", Storage="_Statecode1", DbType="NVarChar(3)")]
public string Statecode1
{
get
{
return this._Statecode1;
}
set
{
if ((this._Statecode1 != value))
{
this._Statecode1 = value;
}
}
}
}
I've tried to replicate your problem, but my example below works fine:
public class HomeController : Controller
{
public ActionResult Index()
{
var viewModel = new IndexViewModel();
viewModel.State = "TX";
return this.View(viewModel);
}
public static IList<StateCodeViewModel> getStatecodesDropDown()
{
var stateCodes = new List<string> { "AX", "GH", "TX" };
var states = from p in stateCodes
select new StateCodeViewModel
{
Value = p,
Text = p
};
return states.ToList();
}
}
public class IndexViewModel
{
public string State { get; set; }
}
public class StateCodeViewModel
{
public string Value { get; set; }
public string Text { get; set; }
}
View
#using MVCWorkbench.Controllers
#model IndexViewModel
#{
ViewBag.Title = "title";
}
#Html.DropDownListFor(model => model.State,
new SelectList(HomeController.getStatecodesDropDown(),
"Value",
"Text",
Model.State.ToString().Trim()
)
)
Not sure what to suggest other then check that the State value is in the dropdown list. Also this could be a case-sensitivity issue perhaps?