Inline Editing
Inline editing in the Grid provides a user-friendly way to modify data directly within a row. Users can initiate and complete the editing process by clicking command buttons on each row, allowing for quick and efficient data updates.
Read the Grid Editing Overview article first.
Basics
To enable the Inline Grid editing, set the Mode()
option of the Editable()
configuration to GridEditMode.InLine
. During Inline editing, only one table row is in edit mode at a time. Users can:
- Click the Edit command button to switch the desired row to edit mode.
- Press
Tab
orShift + Tab
to focus the next or previous editable cell. - Click the Save command button to confirm the current row changes and exit edit mode.
- Click the Cancel command button to cancel the current row changes 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 Delete command button to delete the respective row.
Commands
Inline add, edit, and delete operations use the following command buttons:
- Add (toolbar command)
- Delete (column command)
- Edit (column command)
- Save (column command)—Activated when the Grid row is in edit mode.
- Cancel (column command)—Activated when the Grid row is in edit mode.
In Inline edit mode, the Grid commands execute row by row and the corresponding Grid client-side events also fire row by row. This is similar to Popup editing and unlike InCell editing, where commands and events relate to cells.
When validation is not satisfied, clicking the Save command has no effect, but users can still navigate through all editors in the row to complete the editing.
Setting the Inline Edit Mode
The example below shows how to implement Inline Grid CRUD operations with a minimal required setup.
-
Add a new class to the
~/Models
folder, for example,ProductViewModel
:C#using System.ComponentModel.DataAnnotations; public class ProductViewModel { 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
.C#public ActionResult Products_Create([DataSourceRequest]DataSourceRequest request, ProductViewModel product) { if (ModelState.IsValid) { using (var northwind = new NorthwindEntities()) { // 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); // Insert the entity in the database. northwind.SaveChanges(); // Get the ProductID generated by the database. product.ProductID = entity.ProductID; } } // Return the inserted product. The Grid needs the generated ProductID. Also, return any validation errors through the ModelState. return Json(new[] { product }.ToDataSourceResult(request, ModelState)); }
-
Add an action method to
HomeController.cs
, responsible for saving the edited data items. Name the methodProducts_Update
.C#public ActionResult Products_Update([DataSourceRequest]DataSourceRequest request, ProductViewModel product) { if (ModelState.IsValid) { using (var northwind = new NorthwindEntities()) { // 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 }; // 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 entity in the database. northwind.SaveChanges(); } } // Return the updated product to the Grid. Also, return any validation errors through the ModelState. return Json(new[] { product }.ToDataSourceResult(request, ModelState)); }
-
Add a new action method to
HomeController.cs
, responsible for saving the deleted data items. Name the methodProducts_Destroy
.C#public ActionResult Products_Destroy([DataSourceRequest]DataSourceRequest request, ProductViewModel product) { if (ModelState.IsValid) { using (var northwind = new NorthwindEntities()) { // 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 }; // Attach the entity. northwind.Products.Attach(entity); // Delete the entity. northwind.Products.Remove(entity); // Or use DeleteObject if using a previous versoin of Entity Framework. // northwind.Products.DeleteObject(entity); // Delete the entity in the database. northwind.SaveChanges(); } } // Return the removed product. Also, return any validation errors through the ModelState. return Json(new[] { product }.ToDataSourceResult(request, ModelState)); }
-
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 record with the assignedID
field.Razor@(Html.Kendo().Grid<KendoGridAjaxEditing.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.Edit(); // The "edit" command edits and updates the data items. commands.Destroy(); // The "destroy" command removes data items. }).Title("Commands").Width(200); }) .ToolBar(toolbar => toolbar.Create()) // The "create" command adds new data items. .Editable(editable => editable.Mode(GridEditMode.InLine)) // Enable the Inline edit mode. .DataSource(dataSource => dataSource .Ajax() .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 invoked when the user saves a new data item. .Read(read => read.Action("Products_Read", "Home")) // Action invoked when the Grid requests the data. .Update(update => update.Action("Products_Update", "Home")) // Action invoked when the user saves an updated data item. .Destroy(destroy => destroy.Action("Products_Destroy", "Home")) // Action invoked when the user removes a data item. ) .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. )
-
Specify the name of the created view (CategoryEditor) in the
EditorTemplateName()
option of the column.Razor@(Html.Kendo().Grid<KendoGridAjaxEditing.Models.ProductViewModel>() .Name("grid") .Columns(columns => { columns.Bound(product => product.CategoryID).EditorTemplateName("CategoryEditor"); ... }) // Other configuration. )
For more use cases and information on the column editor templates, refer to the Editor Templates documentation.