This example demonstrates how to implement cascading combo box editors in ASPxGridView.
Setup the Grid and its Column Editors
Create an ASPxGridView control, set the grid's edit mode to Batch
, and add two columns of the GridViewDataComboBoxColumn type. Add a ASPxHiddenField control to the markup.
XML<dx:ASPxHiddenField runat="server" ID="hf" ClientInstanceName="hf"></dx:ASPxHiddenField>
<dx:ASPxGridView ID="grid" ClientInstanceName="grid" runat="server" KeyFieldName="CustomerId">
<Columns>
<%--...--%>
<dx:GridViewDataComboBoxColumn Caption="Country" FieldName="CountryId">
<PropertiesComboBox EnableCallbackMode="true" ValueField="CountryId" TextField="CountryName" ValueType="System.Int32">
<%--...--%>
</PropertiesComboBox>
</dx:GridViewDataComboBoxColumn>
<dx:GridViewDataComboBoxColumn Caption="City" FieldName="CityId">
<PropertiesComboBox EnableCallbackMode="true" CallbackPageSize="20" ValueType="System.Int32" TextField="CityName" ValueField="CityId" ... />
</dx:GridViewDataComboBoxColumn>
</Columns>
<%--...--%>
<SettingsEditing Mode="Batch" />
</dx:ASPxGridView>
Manually Update the Secondary Combo Box Items
Handle the grid's client-side BatchEditStartEditing event. In the event handler, get the cell value (the batchEditApi.GetCellValue method) and call the ASPxHiddenField control's method to save this value. Call the secondary editor's PerformCallback method to update its data.
XML<dx:ASPxGridView ID="grid" runat="server"...>
<%--...--%>
<ClientSideEvents BatchEditStartEditing="OnBatchEditStartEditing" />
</dx:ASPxGridView>
JavaScriptvar curentEditingIndex;
var lastCountry = null;
function OnBatchEditStartEditing(s, e) {
curentEditingIndex = e.visibleIndex;
var currentCountry = grid.batchEditApi.GetCellValue(curentEditingIndex, "CountryId");
hf.Set("CurrentCountry", currentCountry);
if (currentCountry != lastCountry && e.focusedColumn.fieldName == "CityId" && currentCountry != null) {
lastCountry = currentCountry;
RefreshData(currentCountry);
}
}
function RefreshData(countryValue) {
hf.Set("CurrentCountry", countryValue);
CityEditor.PerformCallback();
}
Respond to a Selection Change
Handle the primary editor's client-side SelectedIndexChanged event. In this event handler, get the editor value (the GetValue method) and save it to the ASPxHiddenField control's collection. Call the secondary editor's PerformCallback method to send a callback to the server.
XML<dx:ASPxGridView ID="grid" runat="server"...>
<Columns>
<%--...--%>
<dx:GridViewDataComboBoxColumn Caption="Country" FieldName="CountryId">
<PropertiesComboBox ValueField="CountryId" TextField="CountryName" ValueType="System.Int32">
<ClientSideEvents SelectedIndexChanged="CountriesCombo_SelectedIndexChanged" />
</PropertiesComboBox>
</dx:GridViewDataComboBoxColumn>
<dx:GridViewDataComboBoxColumn Caption="City" FieldName="CityId" />
</Columns>
<%--...--%>
</dx:ASPxGridView>
JavaScriptvar isCustomCascadingCallback = false;
function CountriesCombo_SelectedIndexChanged(s, e) {
lastCountry = s.GetValue();
isCustomCascadingCallback = true;
RefreshData(lastCountry);
}
Filter the Secondary Combo Box Values on the Server Side
In the CellEditorInitialize event handler, access the editors and add a handler to the secondary editor's EndCallback event. In the ItemsRequestedByFilterCondition event handler, filter the secondary editor's data source based on the value from the ASPxHiddenField control's collection and bind the filtered values to the editor.
XML<dx:ASPxGridView ID="grid" runat="server" OnCellEditorInitialize="grid_CellEditorInitialize" ...>
<Columns>
<%--...--%>
<dx:GridViewDataComboBoxColumn Caption="City" FieldName="CityId">
<PropertiesComboBox OnItemsRequestedByFilterCondition="OnItemsRequestedByFilterCondition" ValueType="System.Int32" TextField="CityName" ValueField="CityId">
</Columns>
<%--...--%>
</dx:ASPxGridView>
JavaScriptfunction CitiesCombo_EndCallback(s, e) {
if (isCustomCascadingCallback) {
if (s.GetItemCount() > 0)
grid.batchEditApi.SetCellValue(curentEditingIndex, "CityId", s.GetItem(0).value);
isCustomCascadingCallback = false;
}
}
C#protected void grid_CellEditorInitialize(object sender, ASPxGridViewEditorEventArgs e) {
if(e.Column.FieldName == "CountryId")
e.Editor.ClientInstanceName = "CountryEditor";
if (e.Column.FieldName != "CityId")
return;
var editor = (ASPxComboBox)e.Editor;
editor.ClientInstanceName = "CityEditor";
editor.ClientSideEvents.EndCallback = "CitiesCombo_EndCallback";
}
protected void OnItemsRequestedByFilterCondition(object source, ListEditItemsRequestedByFilterConditionEventArgs e) {
ASPxComboBox editor = source as ASPxComboBox;
IQueryable<City> query;
var take = e.EndIndex - e.BeginIndex + 1;
var skip = e.BeginIndex;
int countryValue = GetCurrentCountry();
if (countryValue > -1)
query = entity.Cities.Where(city => city.CityName.Contains(e.Filter) && city.Country.CountryId == countryValue).OrderBy(city => city.CityId);
else
query = Enumerable.Empty<City>().AsQueryable();
query = query.Skip(skip).Take(take);
editor.DataSource = query.ToList();
editor.DataBind();
}
Documentation
- ASPxGridView
- GridViewDataComboBoxColumn
- ASPxHiddenField
- BatchEditStartEditing
- SelectedIndexChanged
- PerformCallback
- EndCallback
- CellEditorInitialize
Files to Look At
- BatchEditScript.js (VB: BatchEditScript.js)
- Default.aspx (VB: Default.aspx)
- Default.aspx.cs (VB: Default.aspx.vb)
More Examples
- Cascading Editors (Batch Editing) Demo
- GridView for ASP.NET MVC- A simple implementation of cascading comboboxes in Batch Edit mode
- Grid View for ASP.NET Web Forms - Cascading Combo Boxes
Example Code
JavaScriptvar curentEditingIndex;
var lastCountry = null;
var isCustomCascadingCallback = false;
function CountriesCombo_SelectedIndexChanged(s, e) {
lastCountry = s.GetValue();
isCustomCascadingCallback = true;
RefreshData(lastCountry);
}
function CitiesCombo_EndCallback(s, e) {
if (isCustomCascadingCallback) {
if (s.GetItemCount() > 0)
grid.batchEditApi.SetCellValue(curentEditingIndex, "CityId", s.GetItem(0).value);
isCustomCascadingCallback = false;
}
}
function OnBatchEditStartEditing(s, e) {
curentEditingIndex = e.visibleIndex;
var currentCountry = grid.batchEditApi.GetCellValue(curentEditingIndex, "CountryId");
hf.Set("CurrentCountry", currentCountry);
if (currentCountry != lastCountry && e.focusedColumn.fieldName == "CityId" && currentCountry != null) {
lastCountry = currentCountry;
RefreshData(currentCountry);
}
}
function RefreshData(countryValue) {
hf.Set("CurrentCountry", countryValue);
CityEditor.PerformCallback();
}
ASPx<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>ASPxGridView - How to implement cascading comboboxes in Batch Edit mode</title>
<script src="BatchEditScript.js"></script>
</head>
<body>
<form id="form1" runat="server">
ASPxGridView - How to implement cascading comboboxes in Batch Edit mode
<dx:ASPxHiddenField runat="server" ID="hf" ClientInstanceName="hf"></dx:ASPxHiddenField>
<dx:ASPxGridView ID="grid" ClientInstanceName="grid" runat="server" OnBatchUpdate="grid_BatchUpdate" KeyFieldName="CustomerId" OnCellEditorInitialize="grid_CellEditorInitialize">
<Columns>
<dx:GridViewCommandColumn ShowNewButtonInHeader="true" ShowDeleteButton="true"></dx:GridViewCommandColumn>
<dx:GridViewDataColumn FieldName="CustomerId" Visible="false">
<EditFormSettings Visible="False" />
</dx:GridViewDataColumn>
<dx:GridViewDataTextColumn Width="140" FieldName="CustomerName">
<PropertiesTextEdit>
<ValidationSettings RequiredField-IsRequired="true" Display="None"></ValidationSettings>
</PropertiesTextEdit>
</dx:GridViewDataTextColumn>
<dx:GridViewDataComboBoxColumn Width="120" Caption="Country" FieldName="CountryId">
<PropertiesComboBox EnableCallbackMode="true" CallbackPageSize="30" ValueField="CountryId" TextField="CountryName" ValueType="System.Int32">
<ValidationSettings RequiredField-IsRequired="true" Display="None"></ValidationSettings>
<ClientSideEvents SelectedIndexChanged="CountriesCombo_SelectedIndexChanged" />
</PropertiesComboBox>
</dx:GridViewDataComboBoxColumn>
<dx:GridViewDataComboBoxColumn Caption="City" FieldName="CityId" Width="120px">
<PropertiesComboBox EnableCallbackMode="true" CallbackPageSize="20" IncrementalFilteringMode="Contains" OnItemRequestedByValue="OnItemRequestedByValue" OnItemsRequestedByFilterCondition="OnItemsRequestedByFilterCondition" ValueType="System.Int32" TextField="CityName" ValueField="CityId">
</PropertiesComboBox>
</dx:GridViewDataComboBoxColumn>
</Columns>
<ClientSideEvents BatchEditStartEditing="OnBatchEditStartEditing" />
<SettingsEditing Mode="Batch">
<BatchEditSettings ShowConfirmOnLosingChanges="true" EditMode="Cell" />
</SettingsEditing>
</dx:ASPxGridView>
</form>
</body>
</html>
C#using System;
using DevExpress.Web;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
public partial class _Default : System.Web.UI.Page
{
WorldCitiesEntities entity = new WorldCitiesEntities();
protected void Page_Init(object sender, EventArgs e)
{
((GridViewDataComboBoxColumn)grid.Columns["CountryId"]).PropertiesComboBox.DataSource = entity.Countries.ToList();
grid.DataSource = entity.Customers.ToList();
if (!IsPostBack)
{
grid.DataBind();
}
}
protected void grid_CellEditorInitialize(object sender, ASPxGridViewEditorEventArgs e)
{
if (e.Column.FieldName == "CountryId")
e.Editor.ClientInstanceName = "CountryEditor";
if (e.Column.FieldName != "CityId")
return;
var editor = (ASPxComboBox)e.Editor;
editor.ClientInstanceName = "CityEditor";
editor.ClientSideEvents.EndCallback = "CitiesCombo_EndCallback";
}
protected void OnItemRequestedByValue(object source, ListEditItemRequestedByValueEventArgs e)
{
int id;
if (e.Value == null || !int.TryParse(e.Value.ToString(), out id))
return;
ASPxComboBox editor = source as ASPxComboBox;
var query = entity.Cities.Where(city => city.CityId == id);
editor.DataSource = query.ToList();
editor.DataBind();
}
protected void OnItemsRequestedByFilterCondition(object source, ListEditItemsRequestedByFilterConditionEventArgs e)
{
ASPxComboBox editor = source as ASPxComboBox;
IQueryable<City> query;
var take = e.EndIndex - e.BeginIndex + 1;
var skip = e.BeginIndex;
int countryValue = GetCurrentCountry();
if (countryValue > -1)
query = entity.Cities.Where(city => city.CityName.Contains(e.Filter) && city.Country.CountryId == countryValue).OrderBy(city => city.CityId);
else
query = Enumerable.Empty<City>().AsQueryable();
query = query.Skip(skip).Take(take);
editor.DataSource = query.ToList();
editor.DataBind();
}
private int GetCurrentCountry()
{
object id = null;
if (hf.TryGet("CurrentCountry", out id))
return Convert.ToInt32(id);
return -1;
}
protected void grid_BatchUpdate(object sender, DevExpress.Web.Data.ASPxDataBatchUpdateEventArgs e)
{
throw new DemoException("Data modifications are not allowed in the online example. If you need to test the data editing functionality, please download the example on your machine and run it locally.");
foreach (var args in e.InsertValues)
InsertNewItem(args.NewValues);
foreach (var args in e.UpdateValues)
UpdateItem(args.Keys, args.NewValues);
foreach (var args in e.DeleteValues)
DeleteItem(args.Keys, args.Values);
entity.SaveChanges();
e.Handled = true;
}
public void InsertNewItem(OrderedDictionary newValues)
{
Customer customer = new Customer();
LoadNewValues(customer, newValues);
entity.Customers.Add(customer);
}
public void UpdateItem(OrderedDictionary keys, OrderedDictionary newValues)
{
int id = Convert.ToInt32(keys["CustomerId"]);
Customer customer = entity.Customers.Where(x => x.CustomerId == id).FirstOrDefault<Customer>();
LoadNewValues(customer, newValues);
}
public void DeleteItem(OrderedDictionary keys, OrderedDictionary values)
{
int id = Convert.ToInt32(keys["CustomerId"]);
Customer customer = entity.Customers.Where(x => x.CustomerId == id).FirstOrDefault<Customer>();
entity.Customers.Remove(customer);
}
protected void LoadNewValues(Customer item, OrderedDictionary values)
{
item.CustomerName = values["CustomerName"].ToString();
item.CityId = Convert.ToInt32(values["CityId"]);
item.CountryId = Convert.ToInt32(values["CountryId"]);
}
}