Example T155879
Visible to All Users

Grid View for ASP.NET MVC - Implement cascading combo boxes in batch edit mode

This example demonstrates how to create combo box editors and configure the grid's cell edit functionality in batch mode.

Cascading Editors

Overview

Follow the steps below to implement cascading combo boxes in batch edit mode:

  1. Create a combo box column - call the MVCxGridViewColumn.EditorProperties method and add a MVCxColumnComboBoxProperties object.
    Razor
    settings.Columns.Add(c => c.CountryId, country => { country.Caption = "Country"; country.EditorProperties().ComboBox(cs => cs.Assign(ComboBoxPropertiesProvider.Current.CountryComboBoxProperties)); }); settings.Columns.Add(c => c.CityId, city => { city.Caption = "City"; city.EditorProperties().ComboBox(cs => cs.Assign(ComboBoxPropertiesProvider.Current.CityComboBoxProperties)); });
  2. Handle the grid's client-side BatchEditStartEditing event. In the handler, get the cell value and call the secondary combo box editor's PerformCallback method to update the editor's data.
    JavaScript
    function OnBatchEditStartEditing(s, e) { curentEditingIndex = e.visibleIndex; var currentCountry = grid.batchEditApi.GetCellValue(curentEditingIndex, "CountryId"); if (currentCountry != lastCountry && e.focusedColumn.fieldName == "CityId" && currentCountry != null) { lastCountry = currentCountry; grid.GetEditor("CityId").PerformCallback(); } }
  3. Handle the primary editor's client-side SelectedIndexChanged event. In the handler, get the editor's value and send a callback to the server.
    JavaScript
    function CountriesCombo_SelectedIndexChanged(s, e) { lastCountry = s.GetValue(); isCustomCascadingCallback = true; grid.GetEditor("CityId").PerformCallback(); }
  4. Use the GetComboBoxCallbackResult method to get the result of callback processing.
    C#
    public ActionResult ComboBoxCountryPartial(){ return GridViewExtension.GetComboBoxCallbackResult(ComboBoxPropertiesProvider.Current.CountryComboBoxProperties); } public ActionResult ComboBoxCityPartial(){ return GridViewExtension.GetComboBoxCallbackResult(ComboBoxPropertiesProvider.Current.CityComboBoxProperties); }
  5. Handle the secondary editor's EndCallback event to select an item after the callback.
    JavaScript
    function CitiesCombo_EndCallback(s, e) { if (isCustomCascadingCallback) { if (s.GetItemCount() > 0) grid.batchEditApi.SetCellValue(curentEditingIndex, "CityId", s.GetItem(0).value); isCustomCascadingCallback = false; } }

Files to Review

Documentation

More Examples

Does this example address your development requirements/objectives?

(you will be redirected to DevExpress.com to submit your response)

Example Code

Controllers/HomeController.cs(vb)
C#
using System.Web.Mvc; using E4425.Models; using DevExpress.Web.Mvc; using System.Linq; namespace E4425.Controllers { public class HomeController : Controller { WorldCitiesEntities entity = new WorldCitiesEntities(); public ActionResult Index() { return View(entity.Customers.ToList()); } public ActionResult GridViewPartial() { return PartialView(entity.Customers.ToList()); } public ActionResult GridViewEditPartial(MVCxGridViewBatchUpdateValues<Customer, int> modifiedValues) { foreach (var customerInfo in modifiedValues.Insert) { if (modifiedValues.IsValid(customerInfo)) entity.Customers.Add(customerInfo); } foreach (var customerInfo in modifiedValues.Update) { if (modifiedValues.IsValid(customerInfo)) { entity.Customers.Attach(customerInfo); var entry = entity.Entry(customerInfo); entry.Property(e => e.CityId).IsModified = true; entry.Property(e => e.CountryId).IsModified = true; entry.Property(e => e.CustomerName).IsModified = true; } } foreach (var customerID in modifiedValues.DeleteKeys) { entity.Customers.Remove(entity.Customers.Find(customerID)); } // uncomment the next line to enable database updates // entity.SaveChanges(); return PartialView("GridViewPartial", entity.Customers.ToList()); } public ActionResult ComboBoxCountryPartial() { return GridViewExtension.GetComboBoxCallbackResult(ComboBoxPropertiesProvider.Current.CountryComboBoxProperties); } public ActionResult ComboBoxCityPartial() { return GridViewExtension.GetComboBoxCallbackResult(ComboBoxPropertiesProvider.Current.CityComboBoxProperties); } } }
Models/ComboBoxPropertiesProvider.cs(vb)
C#
using DevExpress.Web; using DevExpress.Web.Mvc; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI.WebControls; namespace E4425.Models { public class ComboBoxPropertiesProvider : IDisposable { static ComboBoxPropertiesProvider current; public static ComboBoxPropertiesProvider Current { get { if (current == null) current = new ComboBoxPropertiesProvider(); return current; } } public int EditableCountryID { get { string rawCountryId = HttpContext.Current.Request["CountryId"]; return string.IsNullOrEmpty(rawCountryId) ? -1 : int.Parse(rawCountryId); } } MVCxColumnComboBoxProperties cityComboBoxProperties; public MVCxColumnComboBoxProperties CityComboBoxProperties { get { if (cityComboBoxProperties == null) cityComboBoxProperties = CreateCityComboBoxProperties(); return cityComboBoxProperties; } } MVCxColumnComboBoxProperties countryComboBoxProperties; public MVCxColumnComboBoxProperties CountryComboBoxProperties { get { if (countryComboBoxProperties == null) countryComboBoxProperties = CreateCountryComboBox(); return countryComboBoxProperties; } } WorldCitiesEntities worldCities; protected WorldCitiesEntities WorldCities { get { if (worldCities == null) worldCities = new WorldCitiesEntities(); return worldCities; } } protected MVCxColumnComboBoxProperties CreateCountryComboBox() { MVCxColumnComboBoxProperties cs = new MVCxColumnComboBoxProperties(); cs.CallbackRouteValues = new { Controller = "Home", Action = "ComboBoxCountryPartial" }; cs.Width = Unit.Percentage(100); cs.TextField = "CountryName"; cs.ValueField = "CountryId"; cs.ValueType = typeof(int); cs.IncrementalFilteringDelay = 1000; cs.IncrementalFilteringMode = IncrementalFilteringMode.Contains; cs.FilterMinLength = 2; cs.CallbackPageSize = 20; cs.ClientSideEvents.SelectedIndexChanged = "CountriesCombo_SelectedIndexChanged"; cs.BindList(WorldCities.Countries.ToList()); return cs; } protected MVCxColumnComboBoxProperties CreateCityComboBoxProperties() { MVCxColumnComboBoxProperties cs = new MVCxColumnComboBoxProperties(); cs.CallbackRouteValues = new { Controller = "Home", Action = "ComboBoxCityPartial" }; cs.Width = Unit.Percentage(100); cs.CallbackPageSize = 20; cs.TextField = "CityName"; cs.ValueField = "CityId"; cs.ValueType = typeof(int); cs.IncrementalFilteringDelay = 1000; cs.IncrementalFilteringMode = IncrementalFilteringMode.Contains; cs.FilterMinLength = 2; cs.ValidationSettings.Display = Display.None; cs.BindList(OnItemsRequestedByFilterCondition, OnItemRequestedByValue); cs.ClientSideEvents.BeginCallback = "CitiesCombo_BeginCallback"; cs.ClientSideEvents.EndCallback = "CitiesCombo_EndCallback"; return cs; } protected object OnItemRequestedByValue(ListEditItemRequestedByValueEventArgs e) { int id; if (e.Value == null || !int.TryParse(e.Value.ToString(), out id)) return null; var query = WorldCities.Cities.Where(city => city.CityId == id); return query.ToList(); } protected object OnItemsRequestedByFilterCondition(ListEditItemsRequestedByFilterConditionEventArgs e) { IQueryable<City> query; var skip = e.BeginIndex; var take = e.EndIndex - e.BeginIndex + 1; if (EditableCountryID > -1) query = WorldCities.Cities.Where(city => city.CityName.Contains(e.Filter) && city.Country.CountryId == EditableCountryID).OrderBy(city => city.CityId); else query = WorldCities.Cities.Where(city => city.CityName.Contains(e.Filter)).OrderBy(city => city.CityId); query = query.Skip(skip).Take(take); return query.ToList(); } #region IDisposable Members public void Dispose() { if (this.worldCities != null) worldCities.Dispose(); } #endregion } }
Models/Customer.cs(vb)
C#
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated from a template. // // Manual changes to this file may cause unexpected behavior in your application. // Manual changes to this file will be overwritten if the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ namespace E4425.Models { using System; using System.Collections.Generic; public partial class Customer { public int CustomerId { get; set; } public string CustomerName { get; set; } public Nullable<int> CountryId { get; set; } public Nullable<int> CityId { get; set; } public virtual City City { get; set; } public virtual Country Country { get; set; } } }
Scripts/CascadingComboBoxesBatchEdit.js
JavaScript
var curentEditingIndex; var lastCountry = null; var isCustomCascadingCallback = false; function OnBatchEditStartEditing(s, e) { curentEditingIndex = e.visibleIndex; var currentCountry = grid.batchEditApi.GetCellValue(curentEditingIndex, "CountryId"); if (currentCountry != lastCountry && e.focusedColumn.fieldName == "CityId" && currentCountry != null) { lastCountry = currentCountry; grid.GetEditor("CityId").PerformCallback(); } } function CountriesCombo_SelectedIndexChanged(s, e) { lastCountry = s.GetValue(); isCustomCascadingCallback = true; grid.GetEditor("CityId").PerformCallback(); } function CitiesCombo_BeginCallback(s, e) { e.customArgs['CountryId'] = lastCountry; } function CitiesCombo_EndCallback(s, e) { if (isCustomCascadingCallback) { if (s.GetItemCount() > 0) grid.batchEditApi.SetCellValue(curentEditingIndex, "CityId", s.GetItem(0).value); isCustomCascadingCallback = false; } }
Views/Home/GridViewPartial.cshtml
Razor
@using E4425.Models; @(Html.DevExpress().GridView<Customer>(settings => { settings.Name = "grid"; settings.KeyFields(c => c.CustomerId); settings.Width = System.Web.UI.WebControls.Unit.Pixel(500); settings.CallbackRouteValues = new { Controller = "Home", Action = "GridViewPartial" }; settings.SettingsEditing.Mode = GridViewEditingMode.Batch; settings.SettingsEditing.BatchEditSettings.EditMode = GridViewBatchEditMode.Cell; settings.SettingsEditing.BatchUpdateRouteValues = new { Controller = "Home", Action = "GridViewEditPartial" }; settings.CommandColumn.Visible = true; settings.CommandColumn.ShowNewButtonInHeader = true; settings.CommandColumn.ShowDeleteButton = true; settings.Columns.Add(c => c.CustomerId).Visible = false; settings.Columns.Add(c => c.CustomerName); settings.SettingsEditing.ShowModelErrorsForEditors = true; settings.Columns.Add(c => c.CountryId, country => { country.Caption = "Country"; country.EditorProperties().ComboBox(cs => cs.Assign(ComboBoxPropertiesProvider.Current.CountryComboBoxProperties)); }); settings.Columns.Add(c => c.CityId, city => { city.Caption = "City"; city.EditorProperties().ComboBox(cs => cs.Assign(ComboBoxPropertiesProvider.Current.CityComboBoxProperties)); }); settings.CellEditorInitialize = (s, e) => { ASPxEdit editor = (ASPxEdit)e.Editor; editor.ValidationSettings.Display = Display.None; }; settings.ClientSideEvents.BatchEditStartEditing = "OnBatchEditStartEditing"; }).Bind(Model).GetHtml())
Views/Home/Index.cshtml
Razor
@{ ViewBag.Title = "GridView - How to implement cascading comboboxes in Batch Edit mode"; } <script src="@Url.Content("~/Scripts/CascadingComboBoxesBatchEdit.js")"></script> @using (Html.BeginForm()) { @Html.Partial("GridViewPartial") }

Disclaimer: The information provided on DevExpress.com and affiliated web properties (including the DevExpress Support Center) is provided "as is" without warranty of any kind. Developer Express Inc disclaims all warranties, either express or implied, including the warranties of merchantability and fitness for a particular purpose. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.

Confidential Information: Developer Express Inc does not wish to receive, will not act to procure, nor will it solicit, confidential or proprietary materials and information from you through the DevExpress Support Center or its web properties. Any and all materials or information divulged during chats, email communications, online discussions, Support Center tickets, or made available to Developer Express Inc in any manner will be deemed NOT to be confidential by Developer Express Inc. Please refer to the DevExpress.com Website Terms of Use for more information in this regard.