By default, the batch edit mode does not support the editing of reference property values. However, you can enable this option in your project for a specific reference property. In ASP.NET applications, ASPxGridView performs batch editing on the client side, it is not possible to process or pass values from editors placed inside templates on the server side. Thus, in this example, all processing is performed on the client side.
The following steps demonstrate how to create a custom editor inside the custom ReferencedTemplate template.
Create the following ExampleObject and ReferencedObject business classes. Add the ExampleObject.LookupReferencedObject reference property of the ReferencedObject type.
using DevExpress.Persistent.Base;
//...
[DefaultClassOptions]
public class ExampleObject {
[Browsable(false)]
public Int32 ID { get; protected set; }
public ExampleObject() { }
public string Name { get; set; }
public ReferencedObject LookupReferencedObject { get; set; }
private IObjectSpace objectSpace;
IObjectSpace IObjectSpaceLink.ObjectSpace {
get { return objectSpace; }
set { objectSpace = value; }
}
}
[DefaultClassOptions]
public class ReferencedObject {
[Browsable(false)]
public Int32 ID { get; protected set; }
public ReferencedObject() { }
public string Name { get; set; }
}
Imports DevExpress.Persistent.Base
'...
<DefaultClassOptions>
Public Class ExampleObject
Private privateID As Int32
<Browsable(False)>
Public Property ID() As Int32
Get
Return privateID
End Get
Protected Set(ByVal value As Int32)
privateID = value
End Set
End Property
Public Sub New()
End Sub
Public Property Name() As String
Public Property LookupReferencedObject() As ReferencedObject
Private _objectSpace As IObjectSpace
Private Property IObjectSpaceLink_ObjectSpace() As IObjectSpace Implements IObjectSpaceLink.ObjectSpace
Get
Return _objectSpace
End Get
Set(ByVal value As IObjectSpace)
_objectSpace = value
End Set
End Property
End Class
<DefaultClassOptions>
Public Class ReferencedObject
Private privateID As Int32
<Browsable(False)>
Public Property ID() As Int32
Get
Return privateID
End Get
Protected Set(ByVal value As Int32)
privateID = value
End Set
End Property
Public Sub New()
End Sub
Public Property Name() As String
End Class
Add the ReferencedTemplate class to the Editors folder of the ASP.NET module project. Implement the ITemplate.InstantiateIn method to create a new ASPxComboBox editor containing ReferencedObject items and the “N/A” item.
using System.Web.UI;
using System.Web.UI.WebControls;
using DevExpress.Web;
//...
class ReferencedTemplate : ITemplate {
//Handle the editor's client-side event to emulate the behavior of standard ASPxClientTextEdit.KeyDown grid editor.
const string BatchEditKeyDown =
@"function(s, e) {
var keyCode = ASPxClientUtils.GetKeyCode(e.htmlEvent);
if (keyCode !== ASPx.Key.Tab && keyCode !== ASPx.Key.Enter)
return;
var moveActionName = e.htmlEvent.shiftKey ? 'MoveFocusBackward' : 'MoveFocusForward';
var clientGridView = s.grid;
if (clientGridView.batchEditApi[moveActionName]()) {
ASPxClientUtils.PreventEventAndBubble(e.htmlEvent);
window.batchPreventEndEditOnLostFocus = true;
}
}";
//Handle the editor's client-side event to emulate the behavior of standard ASPxClientEdit.LostFocus grid editor.
const string BatchEditLostFocus =
@"function (s, e) {
var clientGridView = s.grid;
if (!window.batchPreventEndEditOnLostFocus)
clientGridView.batchEditApi.EndEdit();
window.batchPreventEndEditOnLostFocus = false;
}";
public IEnumerable<ReferencedObject> Objects { get; private set; }
public ReferencedTemplate(IEnumerable<ReferencedObject> objects) {
Objects = objects;
}
public void InstantiateIn(Control container) {
GridViewEditItemTemplateContainer gridContainer = (GridViewEditItemTemplateContainer)container;
ASPxComboBox comboBox = new ASPxComboBox();
comboBox.Width = Unit.Percentage(100);
comboBox.ClientInstanceName = "ReferencedEdit";
comboBox.ClientSideEvents.KeyDown = BatchEditKeyDown;
comboBox.ClientSideEvents.LostFocus = BatchEditLostFocus;
ListEditItem notAssignedItem = new ListEditItem("N/A", null);
comboBox.Items.Add(notAssignedItem);
foreach (var currentObject in Objects) {
ListEditItem item = new ListEditItem(currentObject.Name, currentObject.Oid);
comboBox.Items.Add(item);
}
container.Controls.Add(comboBox);
}
}
Imports System.Web.UI
Imports System.Web.UI.WebControls
' ...
Friend Class ReferencedTemplate
Implements ITemplate
'Handle the editor's client-side event to emulate the behavior of standard ASPxClientTextEdit.KeyDown grid editor.
Private Const BatchEditKeyDown As String = "function(s, e) {" & ControlChars.CrLf & _
" var keyCode = ASPxClientUtils.GetKeyCode(e.htmlEvent);" & ControlChars.CrLf & _
" if (keyCode !== ASPxKey.Tab && keyCode !== ASPxKey.Enter) " & ControlChars.CrLf & _
" return;" & ControlChars.CrLf & _
" var moveActionName = e.htmlEvent.shiftKey ? 'MoveFocusBackward' : 'MoveFocusForward';" & ControlChars.CrLf & _
" var clientGridView = s.grid;" & ControlChars.CrLf & _
" if (clientGridView.batchEditApi[moveActionName]()) {" & ControlChars.CrLf & _
" ASPxClientUtils.PreventEventAndBubble(e.htmlEvent);" & ControlChars.CrLf & _
" window.batchPreventEndEditOnLostFocus = true;" & ControlChars.CrLf & _
" }" & ControlChars.CrLf & _
" }"
'Handle the editor's client-side event to emulate the behavior of standard ASPxClientEdit.LostFocus grid editor.
Private Const BatchEditLostFocus As String = "function (s, e) {" & ControlChars.CrLf & _
" var clientGridView = s.grid;" & ControlChars.CrLf & _
" if (!window.batchPreventEndEditOnLostFocus)" & ControlChars.CrLf & _
" clientGridView.batchEditApi.EndEdit();" & ControlChars.CrLf & _
" window.batchPreventEndEditOnLostFocus = false;" & ControlChars.CrLf & _
" }"
Private privateObjects As IEnumerable(Of ReferencedObject)
Public Property Objects() As IEnumerable(Of ReferencedObject)
Get
Return privateObjects
End Get
Private Set(ByVal value As IEnumerable(Of ReferencedObject))
privateObjects = value
End Set
End Property
Public Sub New(ByVal objects As IEnumerable(Of ReferencedObject))
Me.Objects = objects
End Sub
Public Sub InstantiateIn(ByVal container As Control) Implements ITemplate.InstantiateIn
Dim comboBox As New ASPxComboBox()
comboBox.Width = Unit.Percentage(100)
comboBox.ClientInstanceName = "ReferencedEdit"
comboBox.ClientSideEvents.KeyDown = BatchEditKeyDown
comboBox.ClientSideEvents.LostFocus = BatchEditLostFocus
Dim gridContainer As GridViewEditItemTemplateContainer = CType(container, GridViewEditItemTemplateContainer)
Dim notAssignedItem As New ListEditItem("N/A", "null")
comboBox.Items.Add(notAssignedItem)
For Each currentObject In Objects
Dim item As New ListEditItem(currentObject.Name, currentObject.ID)
comboBox.Items.Add(item)
Next currentObject
container.Controls.Add(comboBox)
End Sub
End Class
Finally, create the CreateCustomEditItemTemplateController in the Controllers folder of the ASP.NET module project to subscribe to client-side events and update cell values as demonstrates below.
using DevExpress.ExpressApp.Web.Editors.ASPx;
using DevExpress.ExpressApp.Web.Utils;
//...
public class CreateCustomEditItemTemplateController : ViewController<ListView> {
//Handle the client-side event to set the grid's cell values to the editor.
const string BatchEditStartEditing =
@"function(s,e) {
var productNameColumn = s.GetColumnByField('LookupReferencedObject.Oid');
if (!e.rowValues.hasOwnProperty(productNameColumn.index))
return;
var cellInfo = e.rowValues[productNameColumn.index];
ReferencedEdit.SetText(cellInfo.text);
ReferencedEdit.SetValue(cellInfo.value);
ReferencedEdit['grid'] = s;
if (e.focusedColumn === productNameColumn) {
ReferencedEdit.SetFocus();
}
}";
//Handle the event to pass the value from the editor to the grid cell.
const string BatchEditEndEditing =
@"function(s,e){
var productNameColumn = s.GetColumnByField('LookupReferencedObject.Oid');
if (!e.rowValues.hasOwnProperty(productNameColumn.index))
return;
var cellInfo = e.rowValues[productNameColumn.index];
cellInfo.value = ReferencedEdit.GetValue();
cellInfo.text = ReferencedEdit.GetText();
ReferencedEdit.SetValue(null);
}";
public CreateCustomEditItemTemplateController() {
TargetObjectType = typeof(ExampleObject);
}
protected override void OnActivated() {
base.OnActivated();
ASPxGridListEditor listEditor = (ASPxGridListEditor)View.Editor;
listEditor.CreateCustomEditItemTemplate += listEditor_CreateCustomEditItemTemplate;
listEditor.CreateCustomGridViewDataColumn += listEditor_CreateCustomGridViewDataColumn;
}
protected override void OnDeactivated() {
ASPxGridListEditor listEditor = (ASPxGridListEditor)View.Editor;
listEditor.CreateCustomEditItemTemplate -= listEditor_CreateCustomEditItemTemplate;
listEditor.CreateCustomGridViewDataColumn -= listEditor_CreateCustomGridViewDataColumn;
base.OnDeactivated();
}
private void listEditor_CreateCustomGridViewDataColumn(object sender, CreateCustomGridViewDataColumnEventArgs e) {
if (e.ModelColumn.PropertyEditorType == typeof(ASPxLookupPropertyEditor)) {
if (e.ModelColumn.PropertyName == "LookupReferencedObject") {
var gridColumn = new GridViewDataComboBoxColumn();
gridColumn.Name = e.ModelColumn.PropertyName;
gridColumn.FieldName = e.ModelColumn.PropertyName + ".Oid";
gridColumn.PropertiesComboBox.ValueType = typeof(int?);
gridColumn.PropertiesComboBox.ValueField = "Oid";
gridColumn.PropertiesComboBox.TextField = "Name";
gridColumn.PropertiesComboBox.DataSource = ObjectSpace.GetObjects<ReferencedObject>();
e.Column = gridColumn;
}
}
}
private void listEditor_CreateCustomEditItemTemplate(object sender, CreateCustomEditItemTemplateEventArgs e) {
if (e.ModelColumn.PropertyName == "LookupReferencedObject") {
IEnumerable<ReferencedObject> referencedObjectsList = ObjectSpace.CreateCollection(typeof(ReferencedObject), null, new SortProperty[] { new SortProperty("Name", DevExpress.Xpo.DB.SortingDirection.Ascending) }).Cast<ReferencedObject>();
e.Template = new ReferencedTemplate(referencedObjectsList);
e.Handled = true;
}
}
protected void BatchValueIsUpdated(object sender, CustomUpdateBatchValueEventArgs e) {
if (e.PropertyName == "LookupReferencedObject.Oid") {
var exampleObject = e.Object as ExampleObject;
if (e.NewValue == null) {
exampleObject.LookupReferencedObject = null;
} else {
exampleObject.LookupReferencedObject = exampleObject.Session.GetObjectByKey<ReferencedObject>(e.NewValue);
}
e.Handled = true;
}
}
protected override void OnViewControlsCreated() {
base.OnViewControlsCreated();
ASPxGridListEditor gridListEditor = View.Editor as ASPxGridListEditor;
gridListEditor.BatchEditModeHelper.CustomUpdateBatchValue += BatchValueIsUpdated;
string Grid_BatchEditStartEditingKey = "BatchEditStartEditingKey";
ClientSideEventsHelper.AssignClientHandlerSafe(gridListEditor.Grid, "BatchEditStartEditing",
BatchEditStartEditing, Grid_BatchEditStartEditingKey);
string Grid_BatchEditEndEditingKey = "BatchEditEndEditingKey";
ClientSideEventsHelper.AssignClientHandlerSafe(gridListEditor.Grid, "BatchEditEndEditing",
BatchEditEndEditing, Grid_BatchEditEndEditingKey);
}
}
Imports DevExpress.ExpressApp.Web.Editors.ASPx
Imports DevExpress.ExpressApp.Web.Utils
Imports System.Collections.Generic
'...
Public Class CreateCustomEditItemTemplateController
Inherits ViewController(Of ListView)
'Handle the client-side event to set the grid's cell values to the editor.
Private Const BatchEditStartEditing As String = "function(s,e) { " & ControlChars.CrLf & _
" var productNameColumn = s.GetColumnByField('LookupReferencedObject.Oid');" & ControlChars.CrLf & _
" if (!e.rowValues.hasOwnProperty(productNameColumn.index))" & ControlChars.CrLf & _
" return;" & ControlChars.CrLf & _
" var cellInfo = e.rowValues[productNameColumn.index];" & ControlChars.CrLf & _
" ReferencedEdit.SetText(cellInfo.text);" & ControlChars.CrLf & _
" ReferencedEdit.SetValue(cellInfo.value);" & ControlChars.CrLf & _
" ReferencedEdit['grid'] = s;" & ControlChars.CrLf & _
" if (e.focusedColumn === productNameColumn) {" & ControlChars.CrLf & _
" ReferencedEdit.SetFocus();" & ControlChars.CrLf & _
" }" & ControlChars.CrLf & _
" }"
'Handle the event to pass the value from the editor to the grid cell.
Private Const BatchEditEndEditing As String = "function(s,e){ " & ControlChars.CrLf & _
" var productNameColumn = s.GetColumnByField('LookupReferencedObject.Oid');" & ControlChars.CrLf & _
" if (!e.rowValues.hasOwnProperty(productNameColumn.index))" & ControlChars.CrLf & _
" return;" & ControlChars.CrLf & _
" var cellInfo = e.rowValues[productNameColumn.index];" & ControlChars.CrLf & _
" cellInfo.value = ReferencedEdit.GetValue();" & ControlChars.CrLf & _
" cellInfo.text = ReferencedEdit.GetText();" & ControlChars.CrLf & _
" ReferencedEdit.SetValue(null);" & ControlChars.CrLf & _
" }"
Public Sub New()
TargetObjectType = GetType(ExampleObject)
End Sub
Protected Overrides Sub OnActivated()
MyBase.OnActivated()
Dim listEditor As ASPxGridListEditor = CType(View.Editor, ASPxGridListEditor)
AddHandler listEditor.CreateCustomEditItemTemplate, AddressOf listEditor_CreateCustomEditItemTemplate
AddHandler listEditor.CreateCustomGridViewDataColumn, AddressOf listEditor_CreateCustomGridViewDataColumn
End Sub
Protected Overrides Sub OnDeactivated()
Dim listEditor As ASPxGridListEditor = CType(View.Editor, ASPxGridListEditor)
RemoveHandler listEditor.CreateCustomEditItemTemplate, AddressOf listEditor_CreateCustomEditItemTemplate
RemoveHandler listEditor.CreateCustomGridViewDataColumn, AddressOf listEditor_CreateCustomGridViewDataColumn
MyBase.OnDeactivated()
End Sub
Private Sub listEditor_CreateCustomGridViewDataColumn(ByVal sender As Object, ByVal e As CreateCustomGridViewDataColumnEventArgs)
If e.ModelColumn.PropertyEditorType Is GetType(ASPxLookupPropertyEditor) Then
If e.ModelColumn.PropertyName = "LookupReferencedObject" Then
Dim gridColumn = New GridViewDataComboBoxColumn()
gridColumn.Name = e.ModelColumn.PropertyName
gridColumn.FieldName = e.ModelColumn.PropertyName & ".Oid"
gridColumn.PropertiesComboBox.ValueType = GetType(Integer?)
gridColumn.PropertiesComboBox.ValueField = "Oid"
gridColumn.PropertiesComboBox.TextField = "Name"
gridColumn.PropertiesComboBox.DataSource = ObjectSpace.GetObjects(Of ReferencedObject)()
e.Column = gridColumn
End If
End If
End Sub
Private Sub listEditor_CreateCustomEditItemTemplate(ByVal sender As Object, ByVal e As CreateCustomEditItemTemplateEventArgs)
If e.ModelColumn.PropertyName = "LookupReferencedObject" Then
Dim referencedObjectsList As IEnumerable(Of ReferencedObject) = ObjectSpace.CreateCollection(GetType(ReferencedObject), Nothing, New SortProperty() { New SortProperty("Name", DevExpress.Xpo.DB.SortingDirection.Ascending) }).Cast(Of ReferencedObject)()
e.Template = New ReferencedTemplate(referencedObjectsList)
e.Handled = True
End If
End Sub
Protected Sub BatchValueIsUpdated(ByVal sender As Object, ByVal e As CustomUpdateBatchValueEventArgs)
If e.PropertyName = "LookupReferencedObject.Oid" Then
Dim exampleObject = TryCast(e.Object, ExampleObject)
If e.NewValue Is Nothing Then
exampleObject.LookupReferencedObject = Nothing
Else
exampleObject.LookupReferencedObject = exampleObject.Session.GetObjectByKey(Of ReferencedObject)(e.NewValue)
End If
e.Handled = True
End If
End Sub
Protected Overrides Sub OnViewControlsCreated()
MyBase.OnViewControlsCreated()
Dim gridListEditor As ASPxGridListEditor = TryCast(View.Editor, ASPxGridListEditor)
AddHandler gridListEditor.BatchEditModeHelper.CustomUpdateBatchValue, AddressOf BatchValueIsUpdated
Dim Grid_BatchEditStartEditingKey As String = "BatchEditStartEditingKey"
ClientSideEventsHelper.AssignClientHandlerSafe(gridListEditor.Grid, "BatchEditStartEditing", BatchEditStartEditing, Grid_BatchEditStartEditingKey)
Dim Grid_BatchEditEndEditingKey As String = "BatchEditEndEditingKey"
ClientSideEventsHelper.AssignClientHandlerSafe(gridListEditor.Grid, "BatchEditEndEditing", BatchEditEndEditing, Grid_BatchEditEndEditingKey)
End Sub
End Class
You can now run the application and see the result. The LookupReferencedObject values can now be edited in the batch mode.