How to: Bind an XPCollection to the Grid
- 7 minutes to read
The following example demonstrates how to bind a collection of Person objects to a grid control at design time and runtime. It’s assumed that a grid control (the XtraGrid) has already been added to a form.
Object Structure
Let’s consider a system for tracking people. Each person has a name and belongs to a specific group (Customer, Client). Information on a person is held by the Person persistent object. It provides the Name and Group properties which specify a person’s name and the group to which they belong. The Group property refers to another persistent object - PersonGroup. This encapsulates a group and contains a single GroupName property.
The Person and PersonGroup persistent objects are implemented as follows:
public class Person : XPObject {
public Person() {
Name = "";
Group = null;
}
public string Name {
get { return fName; }
set { SetPropertyValue(nameof(Name), ref fName, value); }
}
string fName;
public PersonGroup Group {
get { return fGroup; }
set { SetPropertyValue(nameof(Group), ref fGroup, value); }
}
PersonGroup fGroup;
}
public class PersonGroup : XPObject {
public PersonGroup() {
GroupName = "";
}
public string GroupName {
get { return fGroupName; }
set { SetPropertyValue(nameof(GroupName), ref fGroupName, value); }
}
string fGroupName;
}
Add this code to your project and build it before you continue.
In this example the grid control will be used to edit Person objects and these will be represented as records. The following image shows the result of this example (the Person and PersonGroup tables are populated with sample data):
Creating Collection
A grid control can work with persistent objects via the XPCollection component. This represents a collection of persistent objects of a specific type. In this example, the grid control should display Person objects, so a new XPCollection must be created and linked to the Person class.
At design time, add the XPCollection component to a form and set it’s XPCollection.ObjectClassInfo property to refer to the Person class:
As a result, the collection’s XPBaseCollection.DisplayableProperties property is automatically initialized with a list of public properties declared in the Person class. This list specifies which properties will be available at design time for controls that are bound to this collection:
Note that additional items are automatically included in the DisplayableProperties property:
This - represents a reference to a persistent object itself (all persistent objects have a public XPBaseObject.This property. The ‘This’ item represents this property.);
Oid - represents a key field of the Person object (all objects derived from the XPObject have a key field named Oid);
Group! - represents a reference to the object that is referred to by the Group property;
Group!Key - represents a key field of the object that is referred to by the Group property;
The ‘This’, “Group!’ and ‘Group!Key’ fields are normally used to represent data in a lookup control. See the How to: Bind an XPCollection to a LookUp topic for for an example.
To create a collection at runtime you can do the following:
Binding Collection to Grid
Now the grid control can be bound to the created collection via the DataSource property:
After the DataSource property has been initialized the grid automatically creates columns for the fields enumerated in the collection’s XPBaseCollection.DisplayableProperties list. In particular it creates columns for the Oid, Name, Group! and Group!Key fields (the captions for the ‘Group!’ and ‘Group!Key’ fields are automatically set to “Group”).
Note
The grid doesn’t automatically create columns for non-browsable fields (see the System.ComponentModel.BrowsableAttribute attribute) and fields that represent collections of objects. In our example, the ‘This’ field is marked as non-browsable, and the ‘Group’ field represents a collection (in XPO any persistent property is always represented as a collection of objects in the binding mechanism). So the grid didn’t create columns for these fields.
If any field in the bound data source represents a collection, the XtraGrid will represent this collection in a detail level (as a detail view). Notice the master-detail buttons in the image above.
The following code shows how to bind the grid at runtime (by default, a grid view’s OptionsBehavior.AutoPopulateColumns option is set to true, so the grid automatically creates columns for the available fields in the bound datasource):
Accessing Nested Properties
To access the nested properties of persistent objects, the following syntax can be used for the DisplayableProperties property and in bound controls: ‘PropertyName.NestedPropertyName’.
Let’s modify the created collection’s XPBaseCollection.DisplayableProperties property so that it returns a person’s name and an associated group’s name. To do this, set this property to the “Name;Group.GroupName” string.
Repopulate the grid’s column collection. You may need to manually remove columns and reopen the project. As a result the grid only creates columns that correspond to the ‘Name’ and ‘Group.GroupName’ fields. The result is shown below:
xpCollectionPerson.DisplayableProperties = "Name;Group.GroupName";
gridControl1.MainView.PopulateColumns();
When you run the application you should see something similar to the following (the database has been populated with a sample data beforehand):
The ‘Person.Group’ property is not marked as aggregated (with the AggregatedAttribute attribute). So the nested properties (‘Group.GroupName’) are read-only and cannot be edited. Only the ‘Name’ column can be edited. In XPO, changes that are made in a bound control are automatically posted to a data source on row validation.
If you need to be able to assign a group to a person by choosing a value from a list of the available group names, use the LookUp editor in a column. See the How to: Bind an XPCollection to a LookUp topic for an example.
Full code
The complete code is shown below:
using DevExpress.Xpo;
public class Person : XPObject {
public Person() {
Name = "";
Group = null;
}
public string Name {
get { return fName; }
set { SetPropertyValue(nameof(Name), ref fName, value); }
}
string fName;
public PersonGroup Group {
get { return fGroup; }
set { SetPropertyValue(nameof(Group), ref fGroup, value); }
}
PersonGroup fGroup;
}
public class PersonGroup : XPObject {
public PersonGroup() {
GroupName = "";
}
public string GroupName {
get { return fGroupName; }
set { SetPropertyValue(nameof(GroupName), ref fGroupName, value); }
}
string fGroupName;
}
// ...
XPCollection xpCollectionPerson = new XPCollection(typeof(Person));
xpCollectionPerson.DisplayableProperties = "Name;Group.GroupName";
gridControl1.DataSource = xpCollectionPerson;