Update/Insert/Delete/ Sortable Templated Datagrid
My choice for implementing a general purpose DataGrid is to embed the Data into a wapper Tracker<T> containing a bool Changed and two properties of the same type of the Data: Value and OldValue.
- When the Item is deleted Value is set to null but OldValue contains the old data to be deleted in the database. The change is signalled by setting Changed to true.
- When the Item is created Value is set to the newly created data, while OldValue is set to null. The change is signalled by setting Changed to true.
- When the item is modified Value and OldValue contain the two versions of the data item and the change is signalled by setting Changed to true.
The introduction of the wrapper doesn't break the separation of concerns between the View Model and the View because the wrapper isn't needed because of a particular choice about How to render the data, but on the contrary it is needed
to support the controller in reflecting the data changes in the database.
The DataGrid keeps The OldValue between consecutive posts. When the user finally decide to move the changes to the DB he can call the Confirm() method of the Tracker<T> that sets the OldValue to
the same value of the Value and resets the Change bool. Calling Cancel() cancels the modifications by setting the Value equal to the OldValue, and resetting the Change bool.
The grid is able to accept also items that are subclasses of the class T used in the
Tracker<T> (however all Tracker<T> in the collection must use the same class). When the grid is posted the Model Binder is able to reconstruct the original subclass. For an example, of how to edit a list of heterogeneous
elements, please, see the HeterogeneousListEditing example in
the download area.
This control needs reference to MVCControlToolkit.Controls.Core-x.x.x.js and MVCControlToolkit.Controls.Grid-x.x.x.js)
The user can edit several items, delete several items and insert several items before posting. The templated DataGrid supports both old Microsoft validation and unobtrusive validation, however when old Microsoft validation is enabled the user is allowed
to insert just one row before posting. One insertion only mode can always be enforced by setting to
false the optional argument enableMultipleInsert parameter whose default value is
The Datagrid does changes tracking, and store the old values of all fields in hidden fields automatically. If the items contain properties that are not used one can specify explicitely the fields to store through the optional
if one uses a ThemedDataGrid(see the example theme included in the binary distribution).
The list of fields to be passed to the toTrack parameter can be build easily with the help of the
FieldsToTrack class as shown below:
@Html.DataGridFor(m => m.ToDoList, ItemContainerType.tr,
"ToDoEditItem", "ToDoDisplayItem", null, "ToDoInsertItem", "ToDoUndeleteItem",
itemCss: "normalRow", altItemCss: "alternateRow",
toTrack: new FieldsToTrack<ToDoItem>().
Add(f => f.Code).Add(f => f.Name).Add(f => f.Description).Add(m => m.Important)
.Add(f => f.ToDoRoles).Add(f => f.ToDoRole))
User can customize the DataGrid by providing :
- A Partial View or a
template to be used as overall container for the whole grid. This parameter is optional. When it is not provided the helper renders just all items without any html surrounding them(see the example grid included in the
BinaryWithSimpleExamples file in the download area)
- The type of container to be used for the items: div, span, tr, td, ....etc(HTML5 tags are also supported ). The type of Item container can be also decided dynamically by providing a function that returns it based on the current item and on the current item
- A Partial View or a template to be used as Dsiplay View Item Template. Obligatory.
- A Partial View or a template to be used as Insert/Edit View Item Template. Optional; necessary only if editing is required.
- A Partial View or a template to be used for the new row insertion item, when in Display mode(in Edit mode it uses the normal edit template). Optional; necessary
only if insertion is required.
- A Partial View or a template to be used as template for deleted rows. Optional; if omitted deleted rows simply disappear. Typically it is provided to put
and undelete button inside it.
- A function that returns the separator to be used between items as a function of the current item, and of its position. Optional; necessary only if the separator is required.
- By specifying in the itemCss and the altItemCss optional parameters two different Css classes to be applyed, respectively, to odd and even items. If the User either delete or add a new row the Css classes are re-computed
on the client side for the new configuration.
Input fields, or input control that may cause data to to be submitted are allowed only in the Edit View Item Template. If you for some reason need to use Input fields (for example a readonly checkbox) in the other
templates please add them the disabled='disabled' attribute, so they will not be submitted.
Operations on the DataGrid are done with the help of customizable DataButtons that can be of type:
Insert, Edit, Delete, Cancel, ResetRow, and Undelete
- Insert Button. It make a new item appears in edit mode and it san be placed only in the template for the insertion of a new item as shown in the examples below
- Edit Button. It can be inserted only in the template for the display mode and it causes the item goes in edit mode without any post.
- Delete Button. It can be placed only in the template for the display mode and it causes the item be deleted.
- Cancel Button. It can be placed only in the template for the edit mode and it causes all values in the item be reset to their
initial values and the item to go in display mode. Here,
initial values means the values before the item was changed the first time, since
initial values are kept after cllient-server round trips because of the
- Reset Row Button. It is similar to the Cancel Button, but the item remains in edit mode.
- Undelete Button. It can be placed only in the template for deleted items, and causes th item be undeleted.
Validation Helpers showing validation errors for each item can be inserted on both the edit and display templates, however this might cause each error appear twice in the validation summary.
Below a simple example. For a complete example see
The overall Template for the Grid:
<td colspan="4"> TO DO LIST</td>
@(ViewData["Content"] as MvcHtmlString)
the ViewData["Content"] renders all grid items.
The Display Template:
@Html.ValidationMessageFor(m => m.Name, "*") @Model.Name
@Html.ValidationMessageFor(m => m.Description, "*") @Model.Description
@Html.ImgDataButton(DataButtonType.Edit, "../../Content/small_pencil.gif", null)
@Html.LinkDataButton(DataButtonType.Delete, "Delete Item", null) </td>
Please note the use of the DataButtons.
The Edit Template:
@Html.ValidationMessageFor(m => m.Name, "*") @Html.TextBoxFor(m => m.Name)
@Html.ValidationMessageFor(m => m.Description, "*") @: Html.TextBoxFor(m => m.Description)
<td class="editor-label" colspan="2">
@Html.HiddenFor(m => m.Code)
@Html.DataButton(DataButtonType.Cancel, "Cancel Changes", null)
Please note the use of the DataButtons
The insert item template:
<td colspan="4">@Html.DataButton(DataButtonType.Insert, "Insert New To Do Item", null)</td>
Finally, how to insert the Grid in your View:
@Html.DataGridFor(m => m.ToDoList, ItemContainerType.tr,
"ToDoEditItem", "ToDoDisplayItem", "ToDoGrid", "ToDoInsertItem", "ToDoUndeleteItem",
itemCss: "normalRow", altItemCss: "alternateRow")
There are parameters also for specifying the attributes of the edit and display containers, the separators and possibly to specify also the item container dynamically.
The Datagrid can be reset to its initial values if them have not been committed yet by using a reset manipulation button:
@Html.ManipulationButton(ManipulationButtonType.ResetGrid, "Reset", m => m.ToDoList, null, ManipulationButtonStyle.Button)
It is also possible to undelete a single row. To this end one can specify the optional deleted row template through the parameter
deletedTemplateName: , and simply put in it an undelete Data Button.
This way the template will substitute any deleted row, allowing the user to "see" it and possibly to undelete it:
<td class="editor-field" colspan="3">
Deleted Item: @:Model.Name
<td class="editor-label" >
<%Html.DataButton(DataButtonType.Undelete, "Undelete Item", null)