InCell Editing
InCell editing allows users to click Grid data cells and type new values like in Excel. There is no need for command buttons to enter and exit edit mode. Users can quickly move between the editable cells and rows by using keyboard navigation.
The InCell edit mode provides a different user experience, compared to the Inline and Popup edit modes. InCell edit mode can be more convenient for advanced users, fast users, or users who prefer keyboard navigation rather than clicking command buttons.
Read the Grid Editing Overview article first.
Basics
To enable the InCell Grid editing, set the Mode()
option of the Editable()
configuration to GridEditMode.InCell
. During InCell editing, only one table cell is in edit mode at a time. Users can:
- Click a specific cell to enter edit mode.
- Press
Tab
orShift + Tab
to confirm the current value and edit the next or previous cell. - Press
Enter
to confirm the current value and exit edit mode when theNavigatable()
option is enabled. - Press
Esc
to cancel the current change and exit edit mode when theNavigatable()
option is enabled. - Click another cell to confirm the current value and edit the new cell.
- Click outside the Grid to confirm the current value and exit edit mode.
- Perform another Grid operation, for example, paging or sorting, to cancel the current edit operation.
- Click the Add toolbar command to add a new row.
- Click the Save changes toolbar command to submit any pending changes.
- Click the Cancel changes toolbar command to cancel any pending changes.
- Click the Delete command button to delete the respective row.
The non-editable cells do not enter edit mode.
Usually, the Batch()
option of the DataSource is enabled when the Grid is set up for InCell editing. This way, the Create
, Update
, and Destroy
requests are sent in batches. For example, updating two data items triggers a single Update
request instead of two.
Commands
InCell add, edit, and delete operations use the following command buttons:
- Add (toolbar command)
- Delete (column command)
- Save changes (toolbar command)—Activated when a new row is added, a row is deleted, or at least one cell is edited.
- Cancel changes (column command)—Activated when the Grid contains a pending change.
In InCell edit mode, the Grid commands execute cell by cell, and the corresponding Grid client-side events also fire cell by cell.
Setting the InCell Edit Mode
The example below shows how to implement InCell Grid CRUD operations with the simplest and minimal required setup.
-
Add a new class to the
~/Models
folder, for example,ProductViewModel
:C#using System.ComponentModel.DataAnnotations; public class ProductViewModel { // The unique model identifier field. public int ProductID { get; set; } [Required] // The ProductName property is required. public string ProductName { get; set; } public short? UnitsInStock { get; set; } }
-
Open
HomeController.cs
and add a new action method that returns the datasetProducts
as JSON. The Grid makes Ajax requests to this action to read the data.C#public ActionResult Products_Read([DataSourceRequest]DataSourceRequest request) { // ToDataSourceResult works with IEnumerable and IQueryable. using (var northwind = new NorthwindEntities()) { IQueryable<Product> products = northwind.Products; DataSourceResult result = products.ToDataSourceResult(request); return Json(result); } }
-
Add a new action method to
HomeController.cs
, responsible for saving the new data items. Name the methodProducts_Create
. Intercept the added data items as a collection ofProductViewModel
, using the[Bind(Prefix="models")]
.C#public ActionResult Products_Create([DataSourceRequest]DataSourceRequest request, [Bind(Prefix="models")]IEnumerable<ProductViewModel> products) { // Will keep the inserted entities here. Used to return the result later. var entities = new List<Product>(); if (ModelState.IsValid) { using (var northwind = new NorthwindEntities()) { foreach (var product in products) { // Create a new Product entity and set its properties from the posted ProductViewModel. var entity = new Product { ProductName = product.ProductName, UnitsInStock = product.UnitsInStock }; // Add the entity. northwind.Products.Add(entity); // Store the entity for later use. entities.Add(entity); } // Insert the entities in the database. northwind.SaveChanges(); } } // Return the inserted entities. The Grid needs the generated ProductID. Also, return any validation errors through the ModelState. return Json(entities.ToDataSourceResult(request, ModelState, product => new ProductViewModel { ProductID = product.ProductID, ProductName = product.ProductName, UnitsInStock = product.UnitsInStock })); }
-
Add an action method to
HomeController.cs
, responsible for saving the edited data items. Name the methodProducts_Update
. Intercept the modified data items as a collection ofProductViewModel
, using the[Bind(Prefix="models")]
.C#public ActionResult Products_Update([DataSourceRequest]DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<ProductViewModel> products) { // Will keep the updated entities here. Used to return the result later. var entities = new List<Product>(); if (ModelState.IsValid) { using (var northwind = new NorthwindEntities()) { foreach (var product in products) { // Create a new Product entity and set its properties from the posted ProductViewModel. var entity = new Product { ProductID = product.ProductID, ProductName = product.ProductName, UnitsInStock = product.UnitsInStock }; // Store the entity for later use. entities.Add(entity); // Attach the entity. northwind.Products.Attach(entity); // Change its state to Modified so EntityFramework can update the existing product instead of creating a new one. northwind.Entry(entity).State = EntityState.Modified; // Or use ObjectStateManager if using a previous version of EntityFramework. // northwind.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified); } // Update the entities in the database. northwind.SaveChanges(); } } // Return the updated entities. Also, return any validation errors through the ModelState. return Json(entities.ToDataSourceResult(request, ModelState, product => new ProductViewModel { ProductID = product.ProductID, ProductName = product.ProductName, UnitsInStock = product.UnitsInStock })); }
-
Add a new action method to
HomeController.cs
, responsible for saving the deleted data items. Name the methodProducts_Destroy
. Intercept the removed data items as a collection ofProductViewModel
, using the[Bind(Prefix="models")]
.C#public ActionResult Products_Destroy([DataSourceRequest]DataSourceRequest request, [Bind(Prefix = "models")]IEnumerable<ProductViewModel> products) { // Will keep the destroyed entities here. Used to return the result later. var entities = new List<Product>(); if (ModelState.IsValid) { using (var northwind = new NorthwindEntities()) { foreach (var product in products) { // Create a new Product entity and set its properties from the posted ProductViewModel. var entity = new Product { ProductID = product.ProductID, ProductName = product.ProductName, UnitsInStock = product.UnitsInStock }; // Store the entity for later use. entities.Add(entity); // Attach the entity. northwind.Products.Attach(entity); // Delete the entity. northwind.Products.Remove(entity); // Or use DeleteObject if using a previous version of EntityFramework. // northwind.Products.DeleteObject(entity); } // Delete the entity in the database. northwind.SaveChanges(); } } // Return the destroyed entities. Also, return any validation errors through the ModelState. return Json(entities.ToDataSourceResult(request, ModelState, product => new ProductViewModel { ProductID = product.ProductID, ProductName = product.ProductName, UnitsInStock = product.UnitsInStock })); }
-
Within the view, configure the Grid to use the action methods created in the previous steps. The
Update
andDestroy
actions must return a collection with the modified or deleted records to enable the DataSource to apply the changes accordingly. TheCreate
method must return a collection of the created records where each data item has an assignedID
field.Razor@(Html.Kendo().Grid<KendoGridBatchEditing.Models.ProductViewModel>() .Name("grid") .Columns(columns => { columns.Bound(product => product.ProductID).Width(100); columns.Bound(product => product.ProductName); columns.Bound(product => product.UnitsInStock).Width(250); columns.Command(commands => { commands.Destroy(); // The "destroy" command removes data items. }).Title("Commands").Width(200); }) .ToolBar(toolbar => { toolbar.Create(); // The "create" command adds new data items. toolbar.Save(); // The "save" command saves the changes. }) .Editable(editable => editable.Mode(GridEditMode.InCell)) // Use InCell editing mode. .DataSource(dataSource => dataSource .Ajax() .Batch(true) // Enable batch updates. .Model(model => { model.Id(product => product.ProductID); // Specify the property, which is the unique identifier of the model that binds to the Grid. model.Field(product => product.ProductID).Editable(false); // Make the "ProductID" property not editable. }) .Create(create => create.Action("Products_Create", "Home")) // Action method invoked when the user saves new data items. .Read(read => read.Action("Products_Read", "Home")) // Action method invoked when the Grid requests the data. .Update(update => update.Action("Products_Update", "Home")) // Action method invoked when the user saves updated data items. .Destroy(destroy => destroy.Action("Products_Destroy", "Home")) // Action method invoked when the user removes data items. ) .Pageable() )
Editors
By default, the Grid component serializes editors allocated within the ~/Views/Shared/EditorTemplates/
folder.
For example, the default editor of the string
properties is the TextBox component, the CheckBox—for booleans, the DateTimePicker—for DateTime
data type properties, and more.
@model object
@Html.Kendo().TextBoxFor(model => model)
If no editors are available in the ~/Views/Shared/EditorTemplates/
folder, the Grid will revert to using a default editor based on the primitive type.
To define a custom editor for a specified column:
-
Add a view that contains the desired editor in the
~/Views/Shared/EditorTemplates/
folder:Razor<!-- ~/Views/Shared/EditorTemplates/CategoryEditor.cshtml--> @model int? @(Html.Kendo().DropDownListFor(m => m) .DataValueField("Id") // The value of the drop-down is taken from the "Id" property. .DataTextField("Name") // The text of the item is taken from the "Name" property. .BindTo((System.Collections.IEnumerable)ViewData["categories"]) // A list of all optipns, which is populated in the controller. )
-
In the model class declaration, decorate the property with the
UIHint
data attribute and specify the name of the created view (CategoryEditor):C#public class ProductViewModel { public int ProductID { get; set; } [Required] public string ProductName { get; set; } public short? UnitsInStock { get; set; } [UIHint("CategoryEditor")] public int? CategoryID { get; set; } }
For a live example, visit the Custom Editing Demo of the Grid.
For more use-cases and information on the column editor templates, refer to the Editor Templates documentation.