Example T146190
Visible to All Users

Grid View for ASP.NET Web Forms - How to update master and detail grids simultaneously in batch edit mode

This example demonstrates how to implement master-detail functionality and update all grid controls on a custom button click in batch edit mode.

Master Detail Grids

Overview

Follow the steps below to update master and detail grids simultaneously:

  1. Create a master grid control, specify its Templates.DetailRow property, and add a detail grid to the template.
  2. Hide default command buttons in the grid's server-side CommandButtonInitialize event handler and use the master grid's StatusBar property to create custom Save Changes and Cancel Changes buttons.
    ASPx
    <Templates> <!-- ... --> <StatusBar> <div style="text-align: right"> <dx:ASPxButton ID="btn" Text="Save Changes" RenderMode="Link" AutoPostBack="false" runat="server"> <ClientSideEvents Click="OnClick" /> </dx:ASPxButton> <dx:ASPxButton ID="btn2" Text="Cancel Changes" RenderMode="Link" AutoPostBack="false" runat="server"> <ClientSideEvents Click="OnCancel" /> </dx:ASPxButton> </div> </StatusBar> </Templates>
    C#
    protected void Grid_CommandButtonInitialize(object sender, ASPxGridViewCommandButtonEventArgs e) { if (e.ButtonType == ColumnCommandButtonType.Update || e.ButtonType == ColumnCommandButtonType.Cancel) { e.Visible = false; } }
  3. Handle the master grid's client-side DetailRowCollapsing and DetailRowExpanding events to get the visible indexes of expanded rows.
    JavaScript
    var visibleIndicies = []; function AddCurrentDetailGrid(visibleIndex) { if (visibleIndicies.indexOf(visibleIndex) == -1) visibleIndicies.push(visibleIndex); } function RemoveCurrentDetailGrid(visibleIndex) { var arrayIndex = visibleIndicies.indexOf(visibleIndex); if (arrayIndex > -1) visibleIndicies.splice(arrayIndex, 1); } function OnExpanding(s, e) { AddCurrentDetailGrid(e.visibleIndex); } function OnCollapsing(s, e) { RemoveCurrentDetailGrid(e.visibleIndex); }
  4. In the custom button's Click event handler, call the master grid's PerformCallback method to update all grid controls on the server.
    JavaScript
    var buttonFlag = false; function OnClick(s, e) { if (visibleIndicies.length == 0) grid.UpdateEdit(); else { buttonFlag = true; grid.PerformCallback(visibleIndicies); } }
  5. Handle the master grid's server-side CustomCallback event. In the handler, call the master grid's FindDetailRowTemplateContol method to access all detail grids and use their UpdateEdit methods to update data.
    C#
    protected void Grid_CustomCallback(object sender, ASPxGridViewCustomCallbackEventArgs e) { ASPxGridView parentGrid = sender as ASPxGridView; parentGrid.UpdateEdit(); parentGrid.DataBind(); if (String.IsNullOrEmpty(e.Parameters)) return; string[] paramArray = e.Parameters.Split(','); for (int i = 0; i < paramArray.Length; i++) { if (String.IsNullOrWhiteSpace(paramArray[i])) continue; ASPxGridView child = parentGrid.FindDetailRowTemplateControl(Convert.ToInt32(paramArray[i]), "grid2") as ASPxGridView; if (child != null) { child.UpdateEdit(); child.DataBind(); } } }

Files to Review

Documentation

More Examples

Example Code

WebSite/Default.aspx
ASPx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ Register Assembly="DevExpress.Web.v14.1, Version=14.1.15.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" Namespace="DevExpress.Web" TagPrefix="dx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <title>How to update a master grid and all its detail grids operating in Batch Edit mode simultaneously</title> <script src="UpdateLogic.js"></script> </head> <body> <form id="frmMain" runat="server"> <b>How to update a master grid and all its detail grids operating in Batch Edit mode simultaneously</b> <dx:ASPxGridView ID="Grid" runat="server" KeyFieldName="CategoryID" OnCommandButtonInitialize="Grid_CommandButtonInitialize" OnCustomCallback="Grid_CustomCallback" ClientInstanceName="grid" OnBatchUpdate="Grid_BatchUpdate" AutoGenerateColumns="False" DataSourceID="nwd1"> <Columns> <dx:GridViewCommandColumn ShowNewButtonInHeader="True" VisibleIndex="0" /> <dx:GridViewDataTextColumn FieldName="CategoryID" ReadOnly="True" VisibleIndex="1"> <EditFormSettings Visible="False" /> </dx:GridViewDataTextColumn> <dx:GridViewDataTextColumn FieldName="CategoryName" VisibleIndex="2"> </dx:GridViewDataTextColumn> <dx:GridViewDataTextColumn FieldName="Description" VisibleIndex="3"> </dx:GridViewDataTextColumn> </Columns> <ClientSideEvents DetailRowExpanding="OnExpanding" DetailRowCollapsing="OnCollapsing" EndCallback="OnEndCallback" CallbackError="OnCallbackError" BatchEditConfirmShowing="OnConfirm" /> <SettingsDetail ShowDetailButtons="true" ShowDetailRow="true" /> <Templates> <DetailRow> <dx:ASPxGridView runat="server" OnCommandButtonInitialize="Grid_CommandButtonInitialize" KeyFieldName="ProductID" OnBatchUpdate="grid_BatchUpdate" OnBeforePerformDataSelect="grid2_BeforePerformDataSelect" ID="grid2" AutoGenerateColumns="False" DataSourceID="nwd2"> <Columns> <dx:GridViewCommandColumn ShowNewButtonInHeader="true" ShowDeleteButton="True" VisibleIndex="0" /> <dx:GridViewDataTextColumn FieldName="ProductID" ReadOnly="True" VisibleIndex="1"> <EditFormSettings Visible="False" /> </dx:GridViewDataTextColumn> <dx:GridViewDataTextColumn FieldName="ProductName" VisibleIndex="2"> </dx:GridViewDataTextColumn> <dx:GridViewDataCheckColumn FieldName="Discontinued" VisibleIndex="4" /> </Columns> <ClientSideEvents BatchEditConfirmShowing="OnConfirm" /> <SettingsEditing Mode="Batch" /> </dx:ASPxGridView> </DetailRow> <StatusBar> <div style="text-align: right"> <dx:ASPxButton ID="btn" Text="Save Changes" RenderMode="Link" AutoPostBack="false" runat="server"> <ClientSideEvents Click="OnClick" /> </dx:ASPxButton> <dx:ASPxButton ID="btn2" Text="Cancel Changes" RenderMode="Link" AutoPostBack="false" runat="server"> <ClientSideEvents Click="OnCancel" /> </dx:ASPxButton> </div> </StatusBar> </Templates> <SettingsEditing Mode="Batch" /> </dx:ASPxGridView> <asp:AccessDataSource ID="nwd1" runat="server" DataFile="~/App_Data/nwind.mdb" DeleteCommand="DELETE FROM [Categories] WHERE [CategoryID] = ?" InsertCommand="INSERT INTO [Categories] ([CategoryName], [Description]) VALUES (?, ?)" SelectCommand="SELECT [CategoryID], [CategoryName], [Description] FROM [Categories]" UpdateCommand="UPDATE [Categories] SET [CategoryName] = ?, [Description] = ? WHERE [CategoryID] = ?"> <DeleteParameters> <asp:Parameter Name="CategoryID" Type="Int32" /> </DeleteParameters> <InsertParameters> <asp:Parameter Name="CategoryName" Type="String" /> <asp:Parameter Name="Description" Type="String" /> </InsertParameters> <UpdateParameters> <asp:Parameter Name="CategoryName" Type="String" /> <asp:Parameter Name="Description" Type="String" /> <asp:Parameter Name="CategoryID" Type="Int32" /> </UpdateParameters> </asp:AccessDataSource> <asp:AccessDataSource ID="nwd2" runat="server" DataFile="~/App_Data/nwind.mdb" DeleteCommand="DELETE FROM [Products] WHERE [ProductID] = ?" InsertCommand="INSERT INTO [Products] ([ProductName], [CategoryID], [Discontinued]) VALUES (?, ?, ?)" SelectCommand="SELECT [ProductID], [ProductName], [CategoryID] , [Discontinued] FROM [Products] WHERE ([CategoryID] = ?)" UpdateCommand="UPDATE [Products] SET [ProductName] = ?, [Discontinued] = ? WHERE [ProductID] = ?"> <DeleteParameters> <asp:Parameter Name="ProductID" Type="Int32" /> </DeleteParameters> <InsertParameters> <asp:Parameter Name="ProductName" Type="String" /> <asp:Parameter Name="CategoryID" Type="Int32" /> <asp:Parameter Name="Discontinued" Type="Boolean" /> </InsertParameters> <SelectParameters> <asp:SessionParameter DefaultValue="?" Name="CategoryID" SessionField="Category" Type="Int32" /> </SelectParameters> <UpdateParameters> <asp:Parameter Name="ProductName" Type="String" /> <asp:Parameter Name="Discontinued" Type="Boolean" /> <asp:Parameter Name="ProductID" Type="Int32" /> </UpdateParameters> </asp:AccessDataSource> <dx:ASPxLabel runat="server" ForeColor="Red" Font-Size="Large" Font-Bold="true" Text="" ID="lbl" ClientInstanceName="lbl"></dx:ASPxLabel> </form> </body> </html>
WebSite/Default.aspx.cs(vb)
C#
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.Linq; using DevExpress.Web.Data; using DevExpress.Web; using System.Data; using System.Web.UI; using System.Web.UI.WebControls; public partial class _Default : System.Web.UI.Page { protected void Grid_BatchUpdate(object sender, ASPxDataBatchUpdateEventArgs e) { foreach (var args in e.InsertValues) InsertNewItem(args.NewValues, true,(ASPxGridView)sender); foreach (var args in e.UpdateValues) UpdateItem(args.Keys, args.NewValues, true); foreach (var args in e.DeleteValues) DeleteItem(args.Keys, args.Values, true); e.Handled = true; } protected void InsertNewItem(OrderedDictionary newValues, bool isParent, ASPxGridView currentGrid) { //comment the bellow line to check data modifications throw new NotImplementedException("Data modifications aren't allowed in online example"); if (isParent) Insert(newValues,nwd1); else{ nwd2.InsertParameters["CategoryID"].DefaultValue = currentGrid.GetMasterRowKeyValue().ToString(); Insert(newValues, nwd2); } } private void Insert(OrderedDictionary newValues, AccessDataSource datasource) { foreach (var item in newValues.Keys) { datasource.InsertParameters[(string)item].DefaultValue = Convert.ToString(newValues[item]); } datasource.Insert(); } protected void UpdateItem(OrderedDictionary keys, OrderedDictionary newValues, bool isParent) { //comment the bellow line to check data modifications throw new NotImplementedException("Data modifications aren't allowed in online example"); if (isParent) Update(keys, newValues, nwd1); else Update(keys, newValues, nwd2); } private void Update(OrderedDictionary keys, OrderedDictionary newValues, AccessDataSource datasource) { foreach (var item in newValues.Keys) { datasource.UpdateParameters[(string)item].DefaultValue = Convert.ToString(newValues[item]); } datasource.UpdateParameters[nwd2.UpdateParameters.Count - 1].DefaultValue = Convert.ToString(keys[0]); datasource.Update(); } protected void DeleteItem(OrderedDictionary keys, OrderedDictionary values, bool isParent) { //comment the bellow line to check data modifications throw new NotImplementedException("Data modifications aren't allowed in online example"); if (isParent) { nwd1.DeleteParameters[0].DefaultValue = Convert.ToString(keys["CategoryID"]); nwd1.Delete(); } else { nwd2.DeleteParameters[0].DefaultValue = Convert.ToString(keys["ProductID"]); nwd2.Delete(); } } protected void grid2_BeforePerformDataSelect(object sender, EventArgs e) { ASPxGridView child = sender as ASPxGridView; GridViewDetailRowTemplateContainer container = child.NamingContainer as GridViewDetailRowTemplateContainer; child.ClientInstanceName = "detailGrid" + container.KeyValue; Session["Category"] = child.GetMasterRowKeyValue(); } protected void Grid_CustomCallback(object sender, ASPxGridViewCustomCallbackEventArgs e) { ASPxGridView parentGrid = sender as ASPxGridView; parentGrid.UpdateEdit(); parentGrid.DataBind(); if (String.IsNullOrEmpty(e.Parameters)) return; string[] paramArray = e.Parameters.Split(','); for (int i = 0; i < paramArray.Length; i++) { if (String.IsNullOrWhiteSpace(paramArray[i])) continue; ASPxGridView child = parentGrid.FindDetailRowTemplateControl(Convert.ToInt32(paramArray[i]), "grid2") as ASPxGridView; if (child != null) { child.UpdateEdit(); child.DataBind(); } } } protected void grid_BatchUpdate(object sender, ASPxDataBatchUpdateEventArgs e) { foreach (var args in e.InsertValues) InsertNewItem(args.NewValues, false, (ASPxGridView)sender); foreach (var args in e.UpdateValues) UpdateItem(args.Keys, args.NewValues, false); foreach (var args in e.DeleteValues) DeleteItem(args.Keys, args.Values, false); e.Handled = true; } protected void Grid_CommandButtonInitialize(object sender, ASPxGridViewCommandButtonEventArgs e) { if (e.ButtonType == ColumnCommandButtonType.Update || e.ButtonType == ColumnCommandButtonType.Cancel) { e.Visible = false; } } }
WebSite/UpdateLogic.js
JavaScript
var visibleIndicies = []; var errorMessage = ""; var buttonFlag = false; function OnClick(s, e) { if (visibleIndicies.length == 0) grid.UpdateEdit(); else { buttonFlag = true; grid.PerformCallback(visibleIndicies); } } function OnCancel(s, e) { for (var i = 0; i < visibleIndicies.length; i++) { var currentKey = grid.GetRowKey(visibleIndicies[i]); var childgrid = ASPxClientControl.GetControlCollection().GetByName("detailGrid" + currentKey); if (childgrid != undefined && childgrid != null) { childgrid.CancelEdit(); } } grid.CancelEdit(); } function OnConfirm(s, e) { if (e.requestTriggerID == 'Grid' && buttonFlag) { e.cancel = true; } } function AddCurrentDetailGrid(visibleIndex) { if (visibleIndicies.indexOf(visibleIndex) == -1) visibleIndicies.push(visibleIndex); } function RemoveCurrentDetailGrid(visibleIndex) { var arrayIndex = visibleIndicies.indexOf(visibleIndex); if (arrayIndex > -1) visibleIndicies.splice(arrayIndex, 1); } function OnExpanding(s, e) { AddCurrentDetailGrid(e.visibleIndex); } function OnCollapsing(s, e) { RemoveCurrentDetailGrid(e.visibleIndex); } function OnEndCallback(s, e) { if (buttonFlag) buttonFlag = false; if (errorMessage != "") { lbl.SetText(errorMessage); errorMessage = ""; } else lbl.SetText(""); } function OnCallbackError(s, e) { errorMessage = e.message; e.handled = true; }

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.