windowsforms-732-controls-and-libraries-data-grid-master-detail-working-with-master-detail-relationships-in-code.md
The following example binds the DevExpress WinForms Data Grid to the nwind.mdb database and display a master-detail relationship. This database ships as part of DevExpress WinForms Demos and is located at C:\Users\Public\Documents\DevExpress Demos 2x.x\Components\Data.
using System.Data;
using System.Data.OleDb;
using DevExpress.Utils;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraGrid.Views.Card;
namespace DXDataGridMasterDetailApp {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
public Form1() {
InitializeComponent();
// Creates a connection to the Nwind database.
OleDbConnection connection = new OleDbConnection(
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source = C:\\Users\\Public\\Documents\\DevExpress Demos 25.2\\Components\\Data\\nwind.mdb");
// Creates the data adapters to retrieve data from the Categories and Products data tables.
OleDbDataAdapter adapterCategories = new OleDbDataAdapter(
"SELECT CategoryID, CategoryName, Picture FROM Categories", connection);
OleDbDataAdapter adapterProducts = new OleDbDataAdapter(
"SELECT CategoryID, ProductID, ProductName, UnitPrice FROM Products", connection);
DataSet dataSetNwind = new DataSet();
// Creates DataTable objects that correspond to database tables.
adapterCategories.Fill(dataSetNwind, "Categories");
adapterProducts.Fill(dataSetNwind, "Products");
// Sets up a master-detail relationship between data tables.
DataColumn keyColumn = dataSetNwind.Tables["Categories"].Columns["CategoryID"];
DataColumn foreignKeyColumn = dataSetNwind.Tables["Products"].Columns["CategoryID"];
dataSetNwind.Relations.Add("CategoriesProducts", keyColumn, foreignKeyColumn);
// Binds the Data Grid to a data source.
gridControl1.DataSource = dataSetNwind.Tables["Categories"];
// Forces the Data Grid to initialize its settings.
gridControl1.ForceInitialize();
// Creates a pattern view (CardView) to display detail data.
CardView cardViewProducts = new CardView(gridControl1);
gridControl1.LevelTree.Nodes.Add("CategoriesProducts", cardViewProducts);
// Specifies the detail view's caption (the caption of detail tabs).
cardViewProducts.ViewCaption = "Category Products";
// Hides the CategoryID column from the master view.
gridView1.Columns["CategoryID"].VisibleIndex = -1;
/* Creates a Picture Edit repository item to display images in the Picture column,
* adds it to the Data Grid's RepositoryItems collection, and sets up image settings.
*/
RepositoryItemPictureEdit riPictureEdit = gridControl1.RepositoryItems.Add("PictureEdit") as RepositoryItemPictureEdit;
gridView1.Columns["Picture"].ColumnEdit = riPictureEdit;
riPictureEdit.SizeMode = DevExpress.XtraEditors.Controls.PictureSizeMode.Stretch;
// Specifies the width of the Picture column.
gridView1.Columns["Picture"].Width = 250;
gridView1.Columns["Picture"].OptionsColumn.FixedWidth = true;
// Enables automatic height for master rows.
gridView1.OptionsView.RowAutoHeight = true;
// Creates columns in a detail pattern view (CardView) for all fields in the Products table.
cardViewProducts.PopulateColumns(dataSetNwind.Tables["Products"]);
// Hides the CategoryID column.
cardViewProducts.Columns["CategoryID"].VisibleIndex = -1;
// Formats cell values in the UnitPrice column as currency.
cardViewProducts.Columns["UnitPrice"].DisplayFormat.FormatType = FormatType.Numeric;
cardViewProducts.Columns["UnitPrice"].DisplayFormat.FormatString = "c2";
}
}
}
Imports System.Data
Imports System.Data.OleDb
Imports DevExpress.Utils
Imports DevExpress.XtraEditors.Repository
Imports DevExpress.XtraGrid.Views.Card
Namespace DXDataGridMasterDetailApp
Partial Public Class Form1
Inherits DevExpress.XtraEditors.XtraForm
Public Sub New()
InitializeComponent()
' Creates a connection to the Nwind database.
Dim connection As New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source = C:\Users\Public\Documents\DevExpress Demos 22.2\Components\Data\nwind.mdb")
' Creates the data adapters to retrieve data from the Categories and Products data tables.
Dim adapterCategories As New OleDbDataAdapter("SELECT CategoryID, CategoryName, Picture FROM Categories", connection)
Dim adapterProducts As New OleDbDataAdapter("SELECT CategoryID, ProductID, ProductName, UnitPrice FROM Products", connection)
Dim dataSetNwind As New DataSet()
' Creates DataTable objects that correspond to database tables.
adapterCategories.Fill(dataSetNwind, "Categories")
adapterProducts.Fill(dataSetNwind, "Products")
' Sets up a master-detail relationship between data tables.
Dim keyColumn As DataColumn = dataSetNwind.Tables("Categories").Columns("CategoryID")
Dim foreignKeyColumn As DataColumn = dataSetNwind.Tables("Products").Columns("CategoryID")
dataSetNwind.Relations.Add("CategoriesProducts", keyColumn, foreignKeyColumn)
' Binds the Data Grid to a data source.
gridControl1.DataSource = dataSetNwind.Tables("Categories")
' Forces the Data Grid to initialize its settings.
gridControl1.ForceInitialize()
' Creates a pattern view (CardView) to display detail data.
Dim cardViewProducts As New CardView(gridControl1)
gridControl1.LevelTree.Nodes.Add("CategoriesProducts", cardViewProducts)
' Specifies the detail view's caption (the caption of detail tabs).
cardViewProducts.ViewCaption = "Category Products"
' Hides the CategoryID column from the master view.
gridView1.Columns("CategoryID").VisibleIndex = -1
' Creates a Picture Edit repository item to display images in the Picture column,
' adds it to the Data Grid's RepositoryItems collection, and sets up image settings.
Dim riPictureEdit As RepositoryItemPictureEdit = TryCast(gridControl1.RepositoryItems.Add("PictureEdit"), RepositoryItemPictureEdit)
gridView1.Columns("Picture").ColumnEdit = riPictureEdit
riPictureEdit.SizeMode = DevExpress.XtraEditors.Controls.PictureSizeMode.Stretch
' Specifies the width of the Picture column.
gridView1.Columns("Picture").Width = 250
gridView1.Columns("Picture").OptionsColumn.FixedWidth = True
' Enables automatic height for master rows.
gridView1.OptionsView.RowAutoHeight = True
' Creates columns in a detail pattern view (CardView) for all fields in the Products table.
cardViewProducts.PopulateColumns(dataSetNwind.Tables("Products"))
' Hides the CategoryID column.
cardViewProducts.Columns("CategoryID").VisibleIndex = -1
' Formats cell values in the UnitPrice column as currency.
cardViewProducts.Columns("UnitPrice").DisplayFormat.FormatType = FormatType.Numeric
cardViewProducts.Columns("UnitPrice").DisplayFormat.FormatString = "c2"
End Sub
End Class
End Namespace
The screenshot below shows the result:
The Data Grid handles properties of the IList type (for example, ArrayList, List<T>) as detail views if its GridOptionsDetail.EnableMasterViewMode option is enabled.
Note
The Data Grid does not handle properties of the IList<T>, ICollection<T>, and IEnumerable types as collections. Enable the View’s BaseView.DataController.AllowIEnumerableDetails option before you bind the Data Grid to a data source to handle these properties as collections.
The following example binds the Data Grid to ArrayList (Orders). Data source items are objects of the Order type with the Products collection property. The Data Grid automatically identifies and visualizes a master-detail relation.
using System;
using System.Collections;
using System.ComponentModel.DataAnnotations;
namespace DXDataGridMasterDetailApp {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
public Form1() {
InitializeComponent();
// Binds the Data Grid to a data source (ArrayList descendant).
gridControl1.DataSource = new Orders();
}
}
public class Orders : ArrayList {
public Orders() {
Add(new Order(new ArrayList() {
new Product(){ ProductName = "Product A-1", Price = 78.99 },
new Product(){ ProductName = "Product A-2", Price = 199.99 },
new Product(){ ProductName = "Product A-3", Price = 18.99 },
}) { OrderName = "Order A", OrderDate = DateTime.Today } );
Add(new Order(new ArrayList() {
new Product(){ ProductName = "Product B-1", Price = 25.99 },
new Product(){ ProductName = "Product B-2", Price = 277.99 },
new Product(){ ProductName = "Product B-3", Price = 10.99 },
}) { OrderName = "Order B", OrderDate = DateTime.Today });
Add(new Order(new ArrayList() {
new Product(){ ProductName = "Product C-1", Price = 5.99 },
new Product(){ ProductName = "Product C-2", Price = 14.99 },
new Product(){ ProductName = "Product C-3", Price = 77.99 },
}) { OrderName = "Order C", OrderDate = DateTime.Today });
}
public virtual new Order this[int index] {
get {
return (Order)base[index];
}
}
}
public class Order {
ArrayList products;
public Order(ArrayList productsList) {
products = productsList;
}
public string OrderName { get; set; }
public DateTime OrderDate { get; set; }
public ArrayList Products {
get { return products; }
set { products = value; }
}
}
public class Product {
public string ProductName { get; set; }
[DisplayFormat(DataFormatString = "c2")]
public double Price { get; set; }
}
}
Imports System
Imports System.Collections
Imports System.ComponentModel.DataAnnotations
Namespace DXDataGridMasterDetailApp
Partial Public Class Form1
Inherits DevExpress.XtraEditors.XtraForm
Public Sub New()
InitializeComponent()
' Binds the Data Grid to a data source (ArrayList descendant).
gridControl1.DataSource = New Orders()
End Sub
End Class
Public Class Orders
Inherits ArrayList
Public Sub New()
Add(New Order(New ArrayList() From {
New Product() With {.ProductName = "Product A-1", .Price = 78.99},
New Product() With {.ProductName = "Product A-2", .Price = 199.99},
New Product() With {.ProductName = "Product A-3", .Price = 18.99}
}) { OrderName = "Order A", OrderDate = Date.Today })
Add(New Order(New ArrayList() From {
New Product() With {.ProductName = "Product B-1", .Price = 25.99},
New Product() With {.ProductName = "Product B-2", .Price = 277.99},
New Product() With {.ProductName = "Product B-3", .Price = 10.99}
}) { OrderName = "Order B", OrderDate = Date.Today })
Add(New Order(New ArrayList() From {
New Product() With {.ProductName = "Product C-1", .Price = 5.99},
New Product() With {.ProductName = "Product C-2", .Price = 14.99},
New Product() With {.ProductName = "Product C-3", .Price = 77.99}
}) { OrderName = "Order C", OrderDate = Date.Today })
End Sub
Default Public Overridable Shadows ReadOnly Property Item(ByVal index As Integer) As Order
Get
Return DirectCast(MyBase.Item(index), Order)
End Get
End Property
End Class
Public Class Order
Private products_Renamed As ArrayList
Public Sub New(ByVal productsList As ArrayList)
products_Renamed = productsList
End Sub
Public Property OrderName() As String
Public Property OrderDate() As Date
Public Property Products() As ArrayList
Get
Return products_Renamed
End Get
Set(ByVal value As ArrayList)
products_Renamed = value
End Set
End Property
End Class
Public Class Product
Public Property ProductName() As String
<DisplayFormat(DataFormatString := "c2")>
Public Property Price() As Double
End Class
End Namespace
The image below shows the result:
Handle the following events to load detail data:
|
Event Name
|
Description
| | --- | --- | |
|
Fires for every master row and allows you to specify the number of details. Use the e.RowHandle parameter to identify a master row. Set the e.RelationCount parameter to the number of detail tables (views).
The Data Grid may fire the MasterRowGetRelationCount event with the e.RowHandle equal to GridControl.InvalidRowHandle. This is a service event that allows you to display or hide all master row expand/collapse buttons. Set the e.RelationCount to any positive number to display expand/collapse buttons.
gridView.MasterRowGetRelationCount += (s, e) => {
e.RelationCount = 1;
};
AddHandler gridView.MasterRowGetRelationCount, Sub(s, e)
e.RelationCount = 1
End Sub
| |
|
Fires for each master with details. The MasterRowEmpty event can fire multiple times to the same master row. It depends on how many related details the master row has. The AllowExpandEmptyDetails option must be disabled.
Use the e.RelationIndex parameter to get the processed detail. Set the e.IsEmpty parameter to true if the detail is empty. The Data Grid does not display empty detail views.
gridView.MasterRowGetRelationCount += (s, e) => {
e.RelationCount = 2;
};
gridView.MasterRowEmpty += (s, e) => {
e.IsEmpty = false;
};
AddHandler gridView.MasterRowGetRelationCount, Sub(s, e) e.RelationCount = 2
AddHandler gridView.MasterRowEmpty, Sub(s, e)
e.IsEmpty = False
End Sub
| |
|
Handle this event to specify the relation name for each detail. Use the e.RelationName parameter to specify the relation name.
Important
The level name must exactly match the name and case of the master-detail relation with which the view is associated. If a detail view displays data from a collection property, the level name must match the collection name.
gridView1.MasterRowGetRelationName += (s, e) => {
switch (e.RelationIndex) {
case 0:
e.RelationName = "Order Detail";
break;
case 1:
e.RelationName = "Customer Details";
break;
}
};
AddHandler gridView1.MasterRowGetRelationName, Sub(s, e)
Select Case e.RelationIndex
Case 0
e.RelationName = "Order Detail"
Case 1
e.RelationName = "Customer Details"
End Select
End Sub
| |
|
The Data Grid dynamically creates a clone view based on the settings of its pattern view when a user expands a master row (a clone view is a copy of a pattern view). Use the e.ChildList property to supply data to clone views.
gridView.MasterRowGetChildList += (s, e) => {
e.ChildList = e.RelationIndex == 0 ? SampleData1.GetData().ToList() : SampleData2.GetData().ToList();
};
AddHandler gridView.MasterRowGetChildList, Sub(s, e) e.ChildList = If(e.RelationIndex = 0, SampleData1.GetData().ToList(), SampleData2.GetData().ToList())
|
Run Demo: Master-Detail Mode with Events
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace DXDataGridMasterDetailApp {
public partial class Form1 : DevExpress.XtraEditors.XtraForm {
public Form1() {
InitializeComponent();
gridControl1.DataSource = SampleData.GetData();
gridView1.MasterRowGetRelationCount += (s, e) => {
e.RelationCount = 1;
};
gridView1.MasterRowEmpty += (s, e) => {
e.IsEmpty = false;
};
gridView1.MasterRowGetChildList += (s, e) => {
e.ChildList = SampleData.GetData();
};
gridView1.MasterRowGetRelationName += (s, e) => {
e.RelationName = "Test Relation Name";
};
}
}
public class SampleData {
[Display(Order = -1)]
public int ID { get; set; }
public string Name { get; set; }
public double Length { get; set; }
public bool Mark { get; set; }
public DateTime RecordDate { get; set; }
public static List<SampleData> GetData() {
return new List<SampleData>() {
new SampleData(){ ID = 0, Name = "Bluehead Wrasse", Length = 15.09, Mark = false, RecordDate = DateTime.Now },
new SampleData(){ ID = 1, Name = "Ornate ButterflyFish", Length = 15.09, Mark = true, RecordDate = DateTime.Now },
new SampleData(){ ID = 2, Name = "Senorita", Length = 15.09, Mark = true, RecordDate = DateTime.Now },
new SampleData(){ ID = 3, Name = "Surf Smelt", Length = 15.09, Mark = false, RecordDate = DateTime.Now },
};
}
}
}
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.ComponentModel.DataAnnotations
Namespace DXDataGridMasterDetailApp
Partial Public Class Form1
Inherits DevExpress.XtraEditors.XtraForm
Public Sub New()
InitializeComponent()
gridControl1.DataSource = SampleData.GetData()
AddHandler gridView1.MasterRowGetRelationCount, Sub(s, e) e.RelationCount = 1
AddHandler gridView1.MasterRowEmpty, Sub(s, e) e.IsEmpty = False
AddHandler gridView1.MasterRowGetChildList, Sub(s, e) e.ChildList = SampleData.GetData()
AddHandler gridView1.MasterRowGetRelationName, Sub(s, e) e.RelationName = "Test Relation Name"
End Sub
End Class
Public Class SampleData
<Display(Order := -1)>
Public Property ID() As Integer
Public Property Name() As String
Public Property Length() As Double
Public Property Mark() As Boolean
Public Property RecordDate() As Date
Public Shared Function GetData() As List(Of SampleData)
Return New List(Of SampleData)() From {
New SampleData() With {.ID = 0, .Name = "Bluehead Wrasse", .Length = 15.09, .Mark = False, .RecordDate = Date.Now},
New SampleData() With {.ID = 1, .Name = "Ornate ButterflyFish", .Length = 15.09, .Mark = True, .RecordDate = Date.Now},
New SampleData() With {.ID = 2, .Name = "Senorita", .Length = 15.09, .Mark = True, .RecordDate = Date.Now},
New SampleData() With {.ID = 3, .Name = "Surf Smelt", .Length = 15.09, .Mark = False, .RecordDate = Date.Now}
}
End Function
End Class
End Namespace
The screenshot below shows the result:
This is an alternative to handling master-detail events. Implement the IRelationList or IRelationListEx interface in a data source or .NET data structure (for example, System.Data.DataView). The IRelationList interface exposes the following properties and methods:
The IRelationListEx interface extends the IRelationList with the following methods that allow you to specify the number of master-detail relationships and detail caption for master rows:
public class NWTables : ArrayList, DevExpress.Data.IRelationList {
private DataSet dataSet;
public NWTables(DataSet ds) {
Add(new NWTable(Properties.Resources.SuppliersTable, "Suppliers"));
Add(new NWTable(Properties.Resources.CategoriesTable, "Categories"));
Add(new NWTable(Properties.Resources.EmployeesTable, "Employees"));
Add(new NWTable(Properties.Resources.ShippersTable, "Shippers"));
Add(new NWTable(Properties.Resources.CustomersTable, "Customers"));
Add(new NWTable(Properties.Resources.OrdersTable, "Orders500"));
dataSet = ds;
}
string IRelationList.GetRelationName(int index, int relationIndex) {
if(index >= 0 && index < this.Count)
return this[index].RelationName;
return "";
}
int IRelationList.RelationCount {
get { return dataSet.Tables.Count > 0 ? 1 : 0; }
}
IList IRelationList.GetDetailList(int index, int relationIndex) {
if(dataSet.Tables.Count > 0)
return dataSet.Tables[((NWTable)this[index]).TableName()].DefaultView;
return null;
}
bool IRelationList.IsMasterRowEmpty(int index, int relationIndex) {
return false;
}
public virtual new NWTable this[int index] {
get {return (NWTable)(base[index]);}
}
}
Public Class NWTables
Inherits ArrayList
Implements DevExpress.Data.IRelationList
Private dataSet As DataSet
Public Sub New(ByVal ds As DataSet)
Add(New NWTable(My.Resources.SuppliersTable, "Suppliers"))
Add(New NWTable(My.Resources.CategoriesTable, "Categories"))
Add(New NWTable(My.Resources.EmployeesTable, "Employees"))
Add(New NWTable(My.Resources.ShippersTable, "Shippers"))
Add(New NWTable(My.Resources.CustomersTable, "Customers"))
Add(New NWTable(My.Resources.OrdersTable, "Orders500"))
dataSet = ds
End Sub
Private Function IRelationList_GetRelationName(ByVal index As Integer, ByVal relationIndex As Integer) As String Implements IRelationList.GetRelationName
If index >= 0 AndAlso index < Me.Count Then
Return Me(index).RelationName
End If
Return ""
End Function
Private ReadOnly Property IRelationList_RelationCount() As Integer Implements IRelationList.RelationCount
Get
Return If(dataSet.Tables.Count > 0, 1, 0)
End Get
End Property
Private Function IRelationList_GetDetailList(ByVal index As Integer, ByVal relationIndex As Integer) As IList Implements IRelationList.GetDetailList
If dataSet.Tables.Count > 0 Then
Return dataSet.Tables(CType(Me(index), NWTable).TableName()).DefaultView
End If
Return Nothing
End Function
Private Function IRelationList_IsMasterRowEmpty(ByVal index As Integer, ByVal relationIndex As Integer) As Boolean Implements IRelationList.IsMasterRowEmpty
Return False
End Function
Default Public Overridable Shadows ReadOnly Property Item(ByVal index As Integer) As NWTable
Get
Return DirectCast(MyBase.Item(index), NWTable)
End Get
End Property
End Class
The following example sets up the WinForms Data Grid control to visualize the Categories-Products-Order Details master-detail relationship.
using System;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Card;
using DevExpress.XtraGrid.Views.Grid;
namespace DXDataGridMasterDetailApp {
public partial class Form1 : XtraForm {
GridControl gridControl;
public Form1() {
InitializeComponent();
gridControl = new GridControl();
GridView mainView = new GridView(gridControl);
GridView patternView1 = new GridView(gridControl);
CardView patternView2 = new CardView(gridControl);
GridLevelNode detailLevel1 = new GridLevelNode();
detailLevel1.LevelTemplate = patternView1;
detailLevel1.RelationName = "CategoriesProducts";
GridLevelNode detailLevel2 = new GridLevelNode();
detailLevel2.LevelTemplate = patternView2;
detailLevel2.RelationName = "ProductsOrder Details";
detailLevel1.Nodes.Add(detailLevel2);
gridControl.LevelTree.Nodes.Add(detailLevel1);
gridControl.MainView = mainView;
gridControl.Name = "gridControl1";
gridControl.Dock = DockStyle.Fill;
gridControl.ViewCollection.AddRange(new BaseView[] { mainView, patternView1, patternView2 });
this.Controls.Add(gridControl);
}
private void Form1_Load(object sender, EventArgs e) {
this.order_DetailsTableAdapter1.Fill(this.nwindDataSet1.Order_Details);
this.productsTableAdapter1.Fill(this.nwindDataSet1.Products);
this.categoriesTableAdapter.Fill(this.nwindDataSet1.Categories);
// Bind the Data Grid control to a data source.
gridControl.DataSource = categoriesBindingSource;
}
}
}
Imports System
Imports System.Windows.Forms
Imports DevExpress.XtraEditors
Imports DevExpress.XtraGrid
Imports DevExpress.XtraGrid.Views.Base
Imports DevExpress.XtraGrid.Views.Card
Imports DevExpress.XtraGrid.Views.Grid
Namespace DXDataGridMasterDetailApp
Partial Public Class Form1
Inherits XtraForm
Private gridControl As GridControl
Public Sub New()
InitializeComponent()
gridControl = New GridControl()
Dim mainView As New GridView(gridControl)
Dim patternView1 As New GridView(gridControl)
Dim patternView2 As New CardView(gridControl)
Dim detailLevel1 As New GridLevelNode()
detailLevel1.LevelTemplate = patternView1
detailLevel1.RelationName = "CategoriesProducts"
Dim detailLevel2 As New GridLevelNode()
detailLevel2.LevelTemplate = patternView2
detailLevel2.RelationName = "ProductsOrder Details"
detailLevel1.Nodes.Add(detailLevel2)
gridControl.LevelTree.Nodes.Add(detailLevel1)
gridControl.MainView = mainView
gridControl.Name = "gridControl1"
gridControl.Dock = DockStyle.Fill
gridControl.ViewCollection.AddRange(New BaseView() { mainView, patternView1, patternView2 })
Me.Controls.Add(gridControl)
End Sub
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs)
Me.order_DetailsTableAdapter1.Fill(Me.nwindDataSet1.Order_Details)
Me.productsTableAdapter1.Fill(Me.nwindDataSet1.Products)
Me.categoriesTableAdapter.Fill(Me.nwindDataSet1.Categories)
' Bind the Data Grid control to a data source.
gridControl.DataSource = categoriesBindingSource
End Sub
End Class
End Namespace
The screenshot below shows the result:
Use the Data Grid’s MainView property to get the top-level master view. The grid’s FocusedView property gets or sets the View which is currently focused.
The Data Grid dynamically creates a clone view based on the settings of its pattern view when a user expands a master row (a clone view is a copy of a pattern view). The Data Grid destroys the clone view after the corresponding master row is collapsed.
The Data Grid fires the ViewRegistered event after it creates a clone view. Handle this event to customize the clone view before it is displayed.
gridControl1.ViewRegistered += (s, e) => {
if(e.View.IsDetailView) {
// Customize the clone view.
}
};
AddHandler gridControl1.ViewRegistered, Sub(s, e)
If e.View.IsDetailView Then
' Customize the clone view.
End If
End Sub
Use the master view’s GetDetailView(Int32, Int32) method to get a clone view for a specific master row.
The following example customizes appearance settings of even rows in a clone view based on a condition.
using DevExpress.XtraGrid.Views.Grid;
gridControl1.ViewRegistered += (s, e) => {
if(e.View.IsDetailView) {
(e.View as GridView).OptionsView.EnableAppearanceEvenRow = true;
}
};
gridView1.MasterRowExpanded += (s, e) => {
GridView masterView = s as GridView;
GridView cloneView = masterView.GetDetailView(e.RowHandle, e.RelationIndex) as GridView;
cloneView.Appearance.EvenRow.Options.UseBackColor = true;
cloneView.Appearance.EvenRow.BackColor = cloneView.RowCount > 2 ? Color.LightGreen : Color.Orange;
};
Imports DevExpress.XtraGrid.Views.Grid
AddHandler gridControl1.ViewRegistered, Sub(s, e)
If e.View.IsDetailView Then
TryCast(e.View, GridView).OptionsView.EnableAppearanceEvenRow = True
End If
End Sub
AddHandler gridView1.MasterRowExpanded, Sub(s, e)
Dim masterView As GridView = TryCast(s, GridView)
Dim cloneView As GridView = TryCast(masterView.GetDetailView(e.RowHandle, e.RelationIndex), GridView)
cloneView.Appearance.EvenRow.Options.UseBackColor = True
cloneView.Appearance.EvenRow.BackColor = If(cloneView.RowCount > 2, Color.LightGreen, Color.Orange)
End Sub
The image below shows the result:
Use the Data Grid’s Views property to get a collection of all visible views. This collection includes a master (root) view and clone views displayed for expanded rows. The Data Grid automatically adds/removes clone views to/from this collection as rows expand or collapse.
Use the view’s BaseView.ParentView property to get its parent view. For clone views, the ParentView property returns a master view. For the main and pattern views, the ParentView property returns null ( Nothing in Visual Basic).
Use the master view’s GridView.GetVisibleDetailView method to get the currently visible detail (a clone view) for the specified master row.
Use the Data Grid’s LevelTree property to get a collection of relation levels (GridLevelNode) - a ‘hierarchical’ structure of pattern views in a master-detail relationship.
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Grid;
// Binds the Data Grid to a data source.
gridControl.DataSource = Category.GetMasterDetailData();
// Creates a pattern view and customizes its appearance settings.
GridView detailPatternView = new GridView { ViewCaption = Category.ProductsLevelName };
detailPatternView.Appearance.Row.BackColor = Color.Coral;
detailPatternView.Appearance.Row.Options.UseBackColor = true;
// Adds the pattern view to the Data Grid's ViewCollection.
gridControl.ViewCollection.Add(detailPatternView);
// Associates the pattern view with the Products relation level.
gridControl.LevelTree.Nodes.Add(Category.ProductsLevelName, detailPatternView);
Imports DevExpress.XtraGrid
Imports DevExpress.XtraGrid.Views.Base
Imports DevExpress.XtraGrid.Views.Grid
' Binds the Data Grid to a data source.
gridControl.DataSource = Category.GetMasterDetailData()
' Creates a pattern view and customizes its appearance settings.
Dim detailPatternView As GridView = New GridView With {.ViewCaption = Category.ProductsLevelName}
detailPatternView.Appearance.Row.BackColor = Color.Coral
detailPatternView.Appearance.Row.Options.UseBackColor = True
' Adds the pattern view to the Data Grid's ViewCollection.
gridControl.ViewCollection.Add(detailPatternView)
' Associates the pattern view with the Products relation level.
gridControl.LevelTree.Nodes.Add(Category.ProductsLevelName, detailPatternView)
Use a clone view’s SourceRow method to get its master row. The clone view’s SourceRowHandle property allows you to get the handle of its master row.
Use the parent (master) view’s GetDetailView method to obtain a clone view (detail) for the specified expanded master row. Use the clone view’s data editing API to get and set cell values.
Use the parent view’s GridView.ExpandMasterRow and GridView.CollapseMasterRow methods to expand/collapse the specified master row. The CollapseAllDetails() method collapses all master rows in a view. You can also use the SetMasterRowExpanded method to expand/collapse a master row.
Expand and collapse API also includes the following methods:
Handle the MasterRowExpanding and MasterRowCollapsing events to prevent the user from expanding/collapsing certain rows.
The MasterRowExpanded and MasterRowCollapsed events notify that the user has expanded/collapsed a master row.
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraGrid.Views.Base;
public void NavigateDetails(ColumnView inspectedView) {
if(inspectedView == null) return;
// Prevent excessive visual updates.
inspectedView.BeginUpdate();
try {
GridView gridView = inspectedView as GridView;
// Get the number of data rows in the View.
int dataRowCount;
if(gridView == null)
dataRowCount = inspectedView.RowCount;
else
dataRowCount = gridView.DataRowCount;
// Traverse the View's rows.
for(int rowHandle = 0; rowHandle < dataRowCount; rowHandle++) {
// Place your code here to process the current row.
// ...
if(gridView != null) {
// Get the number of master-detail relationships for the current row.
int relationCount = gridView.GetRelationCount(rowHandle);
// Iterate through master-detail relationships.
for(int relationIndex = 0; relationIndex < relationCount; relationIndex++) {
// Store expansion status of the corresponding detail View.
bool wasExpanded = gridView.GetMasterRowExpandedEx(rowHandle, relationIndex);
// Expand the detail View.
if(!wasExpanded)
gridView.SetMasterRowExpandedEx(rowHandle, relationIndex, true);
// Navigate the detail View.
NavigateDetails((ColumnView)gridView.GetDetailView(rowHandle, relationIndex));
// Restore the row's expansion status.
gridView.SetMasterRowExpandedEx(rowHandle, relationIndex, wasExpanded);
}
}
}
}
finally {
// Enable visual updates.
inspectedView.EndUpdate();
}
}
Imports DevExpress.XtraGrid.Views.Grid
Imports DevExpress.XtraGrid.Views.Base
Public Sub NavigateDetails(ByVal inspectedView As ColumnView)
If inspectedView Is Nothing Then Return
' Prevent excessive visual updates.
inspectedView.BeginUpdate()
Try
Dim gridView As GridView = Nothing
If TypeOf inspectedView Is GridView Then gridView = inspectedView
' Get the number of data rows in the View.
Dim dataRowCount As Integer
If gridView Is Nothing Then
dataRowCount = inspectedView.RowCount
Else
dataRowCount = gridView.DataRowCount
End If
' Traverse the View's rows.
Dim rowHandle As Integer
For rowHandle = 0 To dataRowCount - 1
' Place your code here to process the current row.
' ...
Try
inspectedView.SetRowCellValue(rowHandle, inspectedView.Columns(0), rowHandle)
Catch ex As Exception
End Try
If Not gridView Is Nothing Then
' Get the number of master-detail relationships for the current row.
Dim relationCount As Integer = gridView.GetRelationCount(rowHandle)
' Iterate through master-detail relationships.
Dim relationIndex As Integer
For relationIndex = 0 To relationCount - 1
' Store expansion status of the corresponding detail View.
Dim wasExpanded As Boolean = _
gridView.GetMasterRowExpandedEx(rowHandle, relationIndex)
' Expand the detail View.
If Not wasExpanded Then gridView.SetMasterRowExpandedEx( _
rowHandle, relationIndex, True)
' Navigate the detail View.
NavigateDetails(gridView.GetDetailView(rowHandle, relationIndex))
' Restore the row's expansion status.
gridView.SetMasterRowExpandedEx(rowHandle, relationIndex, wasExpanded)
Next
End If
Next
Finally
' Enable visual updates.
inspectedView.EndUpdate()
End Try
End Sub
This example expands the focused row and all nested details.
Note
The OptionsDetail.AllowOnlyOneMasterRowExpanded property of detail views must be set to true.
using DevExpress.XtraGrid.Views.Grid;
public void RecursiveExpand(GridView masterView, int masterRowHandle) {
try {
var relationCount = masterView.GetRelationCount(masterRowHandle);
for (var index = relationCount - 1; index >= 0; index--) {
masterView.ExpandMasterRow(masterRowHandle, index);
var childView = masterView.GetDetailView(masterRowHandle, index) as GridView;
if (childView != null) {
var childRowCount = childView.DataRowCount;
for (var handle = 0; handle < childRowCount; handle++)
RecursiveExpand(childView, handle);
}
}
}
catch (Exception ex) { }
finally { }
}
Imports DevExpress.XtraGrid.Views.Grid
Imports DevExpress.XtraGrid.Views.Base
Public Sub RecursiveExpand(ByVal masterView As GridView, ByVal masterRowHandle As Integer)
Try
Dim relationCount = masterView.GetRelationCount(masterRowHandle)
For index = relationCount - 1 To 0 Step -1
masterView.ExpandMasterRow(masterRowHandle, index)
Dim childView = TryCast(masterView.GetDetailView(masterRowHandle, index), GridView)
If childView IsNot Nothing Then
Dim childRowCount = childView.DataRowCount
For handle = 0 To childRowCount - 1
RecursiveExpand(childView, handle)
Next handle
End If
Next index
Catch ex As Exception
Finally
End Try
End Sub
Note
The master view’s OptionsDetail.AllowOnlyOneMasterRowExpanded property must be set to true.
using DevExpress.XtraGrid.Views.Grid;
public void ExpandAllMasterRows(GridView View) {
View.BeginUpdate();
try {
int dataRowCount = View.DataRowCount;
for(int rHandle = 0; rHandle < dataRowCount; rHandle ++)
View.SetMasterRowExpanded(rHandle, true);
}
finally {
View.EndUpdate();
}
}
Imports DevExpress.XtraGrid.Views.Grid
Public Sub ExpandAllMasterRows(ByVal View As GridView)
View.BeginUpdate()
Try
Dim dataRowCount As Integer = View.DataRowCount
Dim rHandle As Integer
For rHandle = 0 To dataRowCount - 1
View.SetMasterRowExpanded(rHandle, True)
Next
Finally
View.EndUpdate()
End Try
End Sub
The following example expands all master rows and all detail/nested views after the grid has been loaded. The example uses ExpandAllMasterRows and RecursiveExpand methods implemented in Example 3 and Example 2 respectively. The ExpandAllMasterRows is modified to call the RecursiveExpand method:
Note
The OptionsDetail.AllowOnlyOneMasterRowExpanded property of master and detail views must be set to true.
private void gridControl1_Load(object sender, EventArgs e) {
ExpandAllMasterRows(gridView1);
}
public void ExpandAllMasterRows(GridView View) {
View.BeginUpdate();
try {
int dataRowCount = View.DataRowCount;
for (int rHandle = 0; rHandle < dataRowCount; rHandle++) {
View.SetMasterRowExpanded(rHandle, true);
RecursiveExpand(View, rHandle);
}
}
finally {
View.EndUpdate();
}
}
public void RecursiveExpand(GridView masterView, int masterRowHandle) {
// ...
}
Private Sub gridControl1_Load(ByVal sender As Object, ByVal e As EventArgs)
ExpandAllMasterRows(gridView1)
End Sub
Public Sub ExpandAllMasterRows(ByVal View As GridView)
View.BeginUpdate()
Try
Dim dataRowCount As Integer = View.DataRowCount
For rHandle As Integer = 0 To dataRowCount - 1
View.SetMasterRowExpanded(rHandle, True)
RecursiveExpand(View, rHandle)
Next rHandle
Finally
View.EndUpdate()
End Try
End Sub
Public Sub RecursiveExpand(ByVal masterView As GridView, ByVal masterRowHandle As Integer)
' ...
End Sub
Read the following quick-reference guide for general information and examples:
Master-Detail Mode - DevExpress WinForms Cheat Sheet
See Also