The ASPxGridListEditor does not natively support multi-row editing. It is recommended to either use the single-row inline editing by setting the AllowEdit property of the ListView model to True in the Model Editor, or edit objects in a DetailView. The multi-row editing functionality is rarely required, but since our customers usually experience difficulties implementing it, I decided to create this example.
There are several examples on how to implement this functionality in the ASPxGridView without XAF. From my point of view, the How to perform ASPxGridView instant updating using different editors in the DataItem template example is the most appropriate for XAF, because:
- this approach can be easily implemented using runtime code
- we already use DataItem templates to show data in grid cells.
All functionality is implemented in a single controller - the MultiRowEditingController. It performs the following operations:
1. Creates an ASPxCallback control and adds it to a page. This control is used to send callbacks from client-side editors used in grid cells.
2. Replaces the default DataItemTemplate with a custom one (EditItemTemplate). The custom template is required to show editors for the user input in grid cells. This template is based on the DataItemTemplate class used in XAF by default. The only difference is that controls from this template are always in the Edit mode.
3. Assigns the client-side script that performs a callback when the value is changed to the editors added to grid cells via the custom DataItemTemplate. This is done in the editor's Init event handler, because at this moment, the NamingContainer that contains the key of the bound object is available.
4. Handles the callback sent from the client side and changes the value of a corresponding object's property.
I recommend that you review the following help topics for additional information:
Access Grid Control Properties
ASPxGridView.Templates Property
How to use custom ASPxGridView template in a Web XAF application
Important notes
We have not tested this solution under all possible scenarios, so feel free to modify and test the code to better suit your needs. It this approach does not meet your requirements, implement a custom List Editor or ViewItem based on a custom Web user control with the ASPxGridView control configured at design time, as you would do in a standard non-XAF ASP.NET application.
Question Comments
Added By:
Yuriy Konytskyy at:
6/3/2013 3:44:39 AM I doesn't work in v12.2.10
Added By:
Anatol (DevExpress Support) at:
7/1/2013 4:19:13 AM I have updated the example. Please see implementation details for version 13.1.
Added By:
Sandro Welter (Intelligix) at:
7/5/2013 3:02:00 PM I'm getting the error below.
The error occurred:
Type: NullReferenceException
Message: Object reference not set to an instance of an object.
Data: 0 entries
Stack trace:
Added By:
Anatol (DevExpress Support) at:
1/10/2014 8:32:12 AM The NullReferenceException issue is solved.
Added By:
PHN at:
1/30/2014 10:55:55 AM Hi,
I have some modifications to support ASPxDateTimePropertyEditor,ASPxLookupPropertyEditor:
using System;
using System.Linq;
using System.ComponentModel;
using System.Web.UI;
using DevExpress.ExpressApp.Web.Editors.ASPx;
using DevExpress.Web.ASPxGridView;
using DevExpress.ExpressApp.Web;
using DevExpress.ExpressApp.Editors;
using DevExpress.Web.ASPxClasses;
using DevExpress.ExpressApp.DC;
using DevExpress.Web.ASPxCallback;
using DevExpress.ExpressApp.Model;
using System.Web.UI.WebControls;
using DevExpress.Web.ASPxEditors;
using DevExpress.Xpo;
using DevExpress.ExpressApp;
namespace WebExample.Module.Web
{
[ListEditor(typeof(object), false)]
public class MultiEditASPxGridListEditor : ASPxGridListEditor
{
const String CallbackArgumentFormat = "function (s, e) {{ {0}.PerformCallback(\"{1}|{2}|\" + {3}); }}"; // ASPxCallback, key, fieldName, value
public MultiRowEditASPxGridListEditor(IModelListView model)
: base(model) { }
ASPxCallback callback;
protected override object CreateControlsCore()
{
Panel panel = new Panel();
callback = new ASPxCallback();
callback.ID = ObjectTypeInfo.Type.Name + "aspxCallback1";
callback.ClientInstanceName = ObjectTypeInfo.Type.Name + "_callback1";
callback.Callback += new CallbackEventHandler(callback_Callback);
panel.Controls.Add(callback);
ASPxGridView grid = (ASPxGridView)base.CreateControlsCore();
grid.HtmlDataCellPrepared += new ASPxGridViewTableDataCellEventHandler(grid_HtmlDataCellPrepared);
panel.Controls.Add(grid);
return panel;
}
void grid_HtmlDataCellPrepared(object sender, ASPxGridViewTableDataCellEventArgs e)
{
if (e.DataColumn is GridViewDataColumnWithInfo && IsColumnSupported(((GridViewDataColumnWithInfo)e.DataColumn).Model))
{
e.Cell.Attributes["onclick"] = RenderHelper.EventCancelBubbleCommand;
}
}
protected override ITemplate CreateDataItemTemplate(IModelColumn columnInfo)
{
if (IsColumnSupported(columnInfo))
{
EditModeDataItemTemplate editModeTemplate = (EditModeDataItemTemplate)CreateDefaultColumnTemplate(columnInfo, this, ViewEditMode.Edit);
editModeTemplate.PropertyEditor.ImmediatePostData = false;
editModeTemplate.CustomCreateCellControl += new EventHandler<DevExpress.ExpressApp.Web.Editors.CustomCreateCellControlEventArgs>(editModeTemplate_CustomCreateCellControl);
return editModeTemplate;
}
else
{
return base.CreateDataItemTemplate(columnInfo);
}
}
void editModeTemplate_CustomCreateCellControl(object sender, DevExpress.ExpressApp.Web.Editors.CustomCreateCellControlEventArgs e)
{
if (e.PropertyEditor.Editor is ASPxWebControl)
{
e.PropertyEditor.Editor.Init += new EventHandler((s, args) => Editor_Init(s, args, e.PropertyEditor.Editor));
}
else if (e.PropertyEditor is ASPxLookupPropertyEditor)
{
ASPxLookupPropertyEditor editor = e.PropertyEditor as ASPxLookupPropertyEditor;
editor.DropDownEdit.DropDown.Init += new EventHandler((s, args) => Editor_Init(s, args, e.PropertyEditor.Editor));
}
}
void Editor_Init(object sender, EventArgs e,WebControl baseEditor)
{
ASPxWebControl editor = (ASPxWebControl)sender;
editor.Init -= new EventHandler((s, args) => Editor_Init(s, args, baseEditor));
// Uncomment to remove editors borders
//editor.Border.BorderStyle = BorderStyle.None;
GridViewDataItemTemplateContainer container = baseEditor.NamingContainer as GridViewDataItemTemplateContainer;
var columnInfo = container.Column as GridViewDataColumnWithInfo;
editor.SetClientSideEventHandler("ValueChanged", String.Format(CallbackArgumentFormat,
callback.ClientInstanceName, container.KeyValue, columnInfo.Model.PropertyName, editor is ASPxDateEdit ? "s.GetText()" : "s.GetValue()"));
}
void callback_Callback(object source, DevExpress.Web.ASPxCallback.CallbackEventArgs e)
{
String[] p = e.Parameter.Split('|');
Object key = TypeDescriptor.GetConverter(ObjectTypeInfo.KeyMember.MemberType).ConvertFromString(p[0]);
IMemberInfo member = ObjectTypeInfo.FindMember(p[1]);
Object value = null;
if (typeof(IXPSimpleObject).IsAssignableFrom(member.MemberType))
{
Type memberKeyType = XafTypesInfo.Instance.FindTypeInfo(member.MemberType).KeyMember.MemberType;
int index1 = p[2].LastIndexOf("(");
int index2 = p[2].LastIndexOf(")");
if (index1 > 0 && index2 > index1)
{
string memberKeyText = p[2].Substring(index1 + 1, index2 - index1 - 1);
value = ObjectSpace.GetObjectByKey(member.MemberType, Convert.ChangeType(memberKeyText, memberKeyType));
}
}
else
{
value = TypeDescriptor.GetConverter(member.MemberType).ConvertFromString(p[2]); ;
}
object obj = ObjectSpace.GetObjectByKey(ObjectTypeInfo.Type, key);
member.SetValue(obj, value);
ObjectSpace.CommitChanges();
}
private Type[] supportedPropertyEditorTypes()
{
return new Type[]{
typeof(ASPxStringPropertyEditor),
typeof(ASPxIntPropertyEditor),
typeof(ASPxBooleanPropertyEditor),
typeof(ASPxEnumPropertyEditor),
typeof(ASPxDateTimePropertyEditor),
typeof(ASPxLookupPropertyEditor)
};
}
protected virtual bool IsColumnSupported(IModelColumn model)
{
if (model.GroupIndex >= 0)
{
return false;
}
foreach (Type type in supportedPropertyEditorTypes())
{
if (type.IsAssignableFrom(model.PropertyEditorType))
{
return true;
}
}
return false;
}
// Sorting and grouping are not supported
protected override ColumnWrapper AddColumnCore(IModelColumn columnInfo)
{
ASPxGridViewColumnWrapper columnWrapper = (ASPxGridViewColumnWrapper)base.AddColumnCore(columnInfo);
if (IsColumnSupported(columnWrapper.Column.Model))
{
columnWrapper.Column.Settings.AllowSort = DevExpress.Utils.DefaultBoolean.False;
columnWrapper.Column.Settings.AllowGroup = DevExpress.Utils.DefaultBoolean.False;
}
return columnWrapper;
}
}
}
Added By:
Anatol (DevExpress Support) at:
1/31/2014 12:04:45 AM Thank you for sharing your code. It looks good. I hope it will be useful for others.
Added By:
Apostolis Bekiaris (DevExpress) at:
2/17/2014 7:12:23 AM Added in eXpandFramework 13.2.7.4
Added By:
HEUNGGI LEE at:
6/4/2014 6:10:47 AM Hi,
Is it possible to set width length on each column in GridView? It is ugly...especially Character field grid width is too narrow ..