Component One ASP.NET MVC

ASP.NET MVC CollectionView 101

This page shows how to get started with ASP.Net MVC's CollectionView controls.

Server-side Operations

ComponentOne's MVC Edition CollectionView is a service that implements the ICollectionView interface to display data in data-bound controls, such as FlexGrid. The server side CollectionViewHelper is a service that enables collections to have reading, editing, filtering, grouping and sorting ability, this is similar to .Net CollectionView. CollectionView internally handles sorting, paging, filtering requests by data bound controls on the server unless it is explicitly specified to perform these operation at client-side.
This section describes how to use Create, Read, Update, Delete and BatchEdit actions for CRUD operations. It also demonstrates the DisableServerRead functionality of the ItemSource, this is used to perform actions explicitly at client side.

Getting Started

Steps for getting started with the CollectionView in MVC applications:

  1. Create a new MVC project using the C1 ASP.NET MVC application template.
  2. Add model to the project. This example uses Entity Framework with Northwind database
  3. Add controller and corresponding view to the project.
  4. Create Read and Create Actions in the controller.
  5. Add a control in the view to display data. This example uses FlexGrid.
  6. Bind the FlexGrid using its Bind property to display data.
  7. The Bind property is of type ItemSource which can take a Model or Action URL to fetch data.

This creates a FlexGrid with AJAX binding, the data for FlexGrid is internally wrapped in a CollectionView which has capability to sort, group, filter and page data. The GridReadCategory action of controller is assigned to Bind property of FlexGrid' ItemSource to populate data. This example is using Create property of FlexGrid's ItemSource to assign GridCreateCategory action of controller, this allows adding record to source database by handling Edit request of CollectionViewHelper.

@(Html.C1().FlexGrid().Id("fGRCView").AutoGenerateColumns(false).AllowAddNew(true) .AllowSorting(true).CssClass("grid") .Columns(columns => columns .Add(c => c.Binding("CategoryID")) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) .Bind( ib => ib.Bind(Url.Action("GridReadCategory")) .Create(Url.Action("GridCreateCategory")) ) )
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); public ActionResult GridReadCategory([C1JsonRequest] CollectionViewRequest requestData) { return this.C1Json(CollectionViewHelper.Read(requestData, db.Categories)); } public ActionResult GridCreateCategory([C1JsonRequest]CollectionViewEditRequest requestData) { var category = requestData.OperatingItems.First(); if (category.CategoryName == null) { category.CategoryName = ""; } return Create(requestData, db.Categories); } private ActionResult Create(CollectionViewEditRequest requestData, DbSet data) where T : class { return this.C1Json(CollectionViewHelper.Edit(requestData, item => { string error = string.Empty; bool success = true; try { data.Add(item); db.SaveChanges(); } catch (DbEntityValidationException e) { error = string.Join(",", e.EntityValidationErrors.Select(result => { return string.Join(",", result.ValidationErrors.Select(err => err.ErrorMessage)); })); success = false; } catch (Exception e) { error = e.Message; success = false; } return new CollectionViewItemResult { Error = error, Success = success && ModelState.IsValid, Data = item }; }, () => data.ToList())); } } }

Result (live):

Update

The server side CollectionViewHelper class defines a Edit request to handle updates.

This example shows how to define update action which enables updating record in source database by handling Edit request of CollectionViewHelper. In Flexgrid, the Update action is assigned to the Update property of ItemSource.

@(Html.C1().FlexGrid().Id("fGUCView").AutoGenerateColumns(false) .Columns(columns => columns .Add(c => c.Binding("CategoryID").IsReadOnly(true)) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) .Bind(ib => ib.Bind(Model.Categories) .Update(Url.Action("GridUpdateCategory")) ) )
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); public ActionResult GridUpdateCategory([C1JsonRequest]CollectionViewEditRequest requestData) { return Update(requestData, db.Categories); } private ActionResult Update(CollectionViewEditRequest requestData, DbSet data) where T : class { return this.C1Json(CollectionViewHelper.Edit(requestData, item => { string error = string.Empty; bool success = true; try { db.Entry(item as object).State = EntityState.Modified; db.SaveChanges(); } catch (DbEntityValidationException e) { error = string.Join(",", e.EntityValidationErrors.Select(result => { return string.Join(",", result.ValidationErrors.Select(err => err.ErrorMessage)); })); success = false; } catch (Exception e) { error = e.Message; success = false; } return new CollectionViewItemResult { Error = error, Success = success && ModelState.IsValid, Data = item }; }, () => data.ToList())); } // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Delete

This example shows how to define action in controller to delete rows from database by handling Edit request of CollectionViewHelper. In FlexGrid this action is assigned to the Delete property of ItemSource.

@(Html.C1().FlexGrid().Id("fGDelCView").AutoGenerateColumns(false).IsReadOnly(true) .Columns(columns => columns .Add(c => c.Binding("CategoryID")) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) .AllowDelete(true) .Bind( ib => ib.Bind(Model.Categories) .Delete(Url.Action("GridDeleteCategory")) ) )
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); public ActionResult GridDeleteCategory([C1JsonRequest]CollectionViewEditRequest requestData) { return Delete(requestData, db.Categories, item => item.CategoryID); } private ActionResult Delete(CollectionViewEditRequest requestData, DbSet data, Func getKey) where T : class { return this.C1Json(CollectionViewHelper.Edit(requestData, item => { string error = string.Empty; bool success = true; try { var resultItem = data.Find(getKey(item)); data.Remove(resultItem); db.SaveChanges(); } catch (DbEntityValidationException e) { error = string.Join(",", e.EntityValidationErrors.Select(result => { return string.Join(",", result.ValidationErrors.Select(err => err.ErrorMessage)); })); success = false; } catch (Exception e) { error = e.Message; success = false; } return new CollectionViewItemResult { Error = error, Success = success && ModelState.IsValid, Data = item }; }, () => data.ToList())); } // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

BatchEditing

This example shows how to define batch edit action in controller to update database with collection view. BatchEdit allows to submit multiple changes back to database. This is accomplised by handling BatchEdit request of CollectionViewHelper. In FlexGrid, the BatchEdit action is assigned to BatchEdit property of ItemsSource.

Note: Ensure that DisableServerRead property of databound control's ItemSource is set to true when performing BatchEdit. This stops filtering, paging and sorting operations from sending an update request to the server, which will automatically updates the data in source database. These operations should be done at client side incase of BatchEditing otherwise modified data will also be submitted when user sorts, filters or performs paging.

@(Html.C1().FlexGrid().Id("fGBECView").AutoGenerateColumns(false) .Columns(columns => columns .Add(c => c.Binding("CategoryID")) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) .Bind(ib => ib.DisableServerRead(true).Bind(Model.Categories).BatchEdit(Url.Action("GridBatchEdit"))) .AllowAddNew(true) .AllowDelete(true) .CssClass("grid") )
//Batch Edit function batchUpdate() { var batchEditGrid = wijmo.Control.getControl('#fGBECView'), cv = batchEditGrid.collectionView; cv.commit(); };
using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); public ActionResult GridBatchEdit([C1JsonRequest]CollectionViewBatchEditRequest requestData) { return this.C1Json(CollectionViewHelper.BatchEdit(requestData, batchData => { var itemresults = new List>(); string error = string.Empty; bool success = true; try { if (batchData.ItemsCreated != null) { batchData.ItemsCreated.ToList().ForEach(st => { db.Categories.Add(st); itemresults.Add(new CollectionViewItemResult { Error = "", Success = ModelState.IsValid, Data = st }); }); } if (batchData.ItemsDeleted != null) { batchData.ItemsDeleted.ToList().ForEach(category => { var fCategory = db.Categories.Find(category.CategoryID); db.Categories.Remove(fCategory); itemresults.Add(new CollectionViewItemResult { Error = "", Success = ModelState.IsValid, Data = category }); }); } if (batchData.ItemsUpdated != null) { batchData.ItemsUpdated.ToList().ForEach(category => { db.Entry(category).State = EntityState.Modified; itemresults.Add(new CollectionViewItemResult { Error = "", Success = ModelState.IsValid, Data = category }); }); } db.SaveChanges(); } catch (DbEntityValidationException e) { error = string.Join(",", e.EntityValidationErrors.SelectMany(i => i.ValidationErrors).Select(i => i.ErrorMessage)); success = false; } catch (Exception e) { error = e.Message; success = false; } return new CollectionViewResponse { Error = error, Success = success, OperatedItemResults = itemresults }; }, () => db.Categories.ToList())); } // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Disable Server Reading

DisableServerRead property disables server side synchronisation. When it is set to True, all the items will be transferred to the client side. Sorting, paging or filtering will be done on client side. And the text like waiting... is not shown for loading the data when the scrollbar scrolls. Otherwise, sorting, paging or filtering will be done on server side. And sometimes the waiting... text will be shown.

@(Html.C1().FlexGrid().Id("fGDisableServerView").IsReadOnly(true).AllowSorting(true).AutoGenerateColumns(false) .Bind(b => b.DisableServerRead(true).PageSize(10).Bind(Model.Customers)).CssStyle("height","100%") .Columns(columns => columns .Add(c => c.Binding("CustomerID")) .Add(c => c.Binding("CompanyName")) .Add(c => c.Binding("ContactName")) .Add(c => c.Binding("City")) .Add(c => c.Binding("Country")) .Add(c => c.Binding("Phone")) ) ) @Html.Partial("_Pager", "fGDisableServerView")
$(document).ready(function () { //Disable Server Reading fGDisableServerView = wijmo.Control.getControl('#fGDisableServerView'); }); //Disable Server Read var fGDisableServerView = null;
using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Paging FlexGrid which PageSize is set to 10

Client-side Operations

CollectionView has a powerful client API. CollectionViewHelper internally performes server side operations like sorting, filtering, paging on data for MVC controls like FlexGrid, FlexChart and other Input controls. However it is possible to explicitly perform these operations on client-side. This section demonstrates following client-side operations:- Current Record Management, Sorting, Filtering, Grouping and Tracking Changes.

Note: It is important to note that the DisableServerRead property of ItemSource should be set to True if filtering, paging, sorting is to be performed on data available at client side only. The default setting is False, with default setting: sorting, paging, filtering happen on server, this is done by a internal callback request which is sent to the CollectionViewHelper on the server to perform the said operations.

Refer client api documentation for more detail.

Current Record Management

As implementing the interface ICollectionView, CollectionView can manage the current record.

This example shows how you can manage the current record through APIs provided by the CollectionView class.

In this case, we use the properties currentPosition to obtain the current record position in the collection. We also use the methods moveCurrentTo(item), moveCurrentToFirst(), moveCurrentToLast(), moveCurrentToNext(), moveCurrentToPosition(index) and moveCurrentToPrevious() to change the current position. When the current is changed, we use the events currentChanging and currentChanged to track it. We can cancel the current changing in the event currentChanging.

Notes: Click the "Move To Next" button to move the current to the next one. Click the "Move to Previous" to move the current to the previous on. Clicking the "Stop in 4th Row" button will cause the current is forbidden to be changed when it locates in the 4th row. Then clicking the "Clear Stopping" button will let the current be changed freely.

<div class="row-fluid well btn-group"> <button class="btn btn-default" id="btnCRMMoveNext">Move To Next</button> <button class="btn btn-default" id="btnCRMMovePre">Move To Previous</button> <button class="btn btn-default" id="btnCRMStop4">Stop in 4th Row</button> <button class="btn btn-default" id="btnCRMReset">Clear Stopping</button> </div> @(Html.C1().FlexGrid().Id("crmGrid").IsReadOnly(true).SelectionMode(C1.Web.Mvc.Grid.SelectionMode.Row) .AutoGenerateColumns(true).Bind(b=>b.DisableServerRead(true).Bind(Model.Customers)) )
$(document).ready(function () { //Current Record Management crmGrid = wijmo.Control.getControl('#crmGrid'); cvCRM = crmGrid.itemsSource; //new wijmo.collections.CollectionView(getData(10)), // Add the processes for buttons' click // move the current to the next one document.getElementById('btnCRMMoveNext').addEventListener('click', function () { cvCRM.moveCurrentToNext(); }); // move the current to the preivous one document.getElementById('btnCRMMovePre').addEventListener('click', function () { cvCRM.moveCurrentToPrevious(); }); // when the current item is the 4th one, forbid changing current. document.getElementById('btnCRMStop4').addEventListener('click', function () { cvCRM.currentChanging.addHandler(stopCurrentIn4th); }); // restore to be able to change current. document.getElementById('btnCRMReset').addEventListener('click', function () { cvCRM.currentChanging.removeHandler(stopCurrentIn4th); }); // define the funciton to forbid the current moving. function stopCurrentIn4th(sender, e) { // when the current is the 4rd item, stop moving. if (sender.currentPosition === 3) { e.cancel = true; } }; }); // create collectionview, grid var crmGrid = null , cvCRM = null;
using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Sorting

The CollectionView class supports sorting through the ICollectionView interface, which is identical to the one in .NET. To enable sorting, add one or more sortDescriptions objects to the CollectionView.sortDescriptions property. Then the sorted result can be obtained from the CollectionView.items property.

SortDescription objects are flexible, allowing you to sort data based on value in ascending or descending order. In the sample below, you can sort the collection based on the corresponding field value choosed in the first list. You can also specify the sorting order in the second list.

<div class="row-fluid well row"> <div class="col-md-8"> <select id="sortingFieldNameList" class="form-control"> </select> </div> <div class="col-md-4"> <select id="sortingOrderList" class="form-control"> <option value="true" selected="selected">Ascending</option> <option value="false">Descending</option> </select> </div> </div> @(Html.C1().FlexGrid().Id("sortingGrid").IsReadOnly(true).AllowSorting(false).AutoGenerateColumns(false) .Bind(b => b.DisableServerRead(true).Bind(Model.Customers)) .Columns(columns => columns .Add(c => c.Binding("CustomerID")) .Add(c => c.Binding("CompanyName")) .Add(c => c.Binding("ContactName")) .Add(c => c.Binding("City")) .Add(c => c.Binding("Country")) .Add(c => c.Binding("Phone")) ) )
function getNames() { return ['CustomerID', 'CompanyName', 'ContactName', 'City', 'Country', 'Phone']; }; $(document).ready(function () { //Sorting sortingGrid = wijmo.Control.getControl('#sortingGrid'); cvSorting = sortingGrid.itemsSource; sortingFieldNameList = document.getElementById('sortingFieldNameList'); sortingOrderList = document.getElementById('sortingOrderList'); //sortingNames = getNames(); // initialize the list items for field names and orders. sortingFieldNameList.innerHTML += ''; for (var i = 0; i < sortingNames.length; i++) { sortingFieldNameList.innerHTML += ''; } // track the list change in order to udpate the sortDescriptions property. sortingFieldNameList.addEventListener('change', sortGrid); sortingOrderList.addEventListener('change', sortGrid); }); //Sorting // create collectionview, grid, the jQuery elements, the field name list. var cvSorting = null, sortingGrid =null, sortingFieldNameList = null, sortingOrderList = null, sortingNames = getNames(); function sortGrid() { var fieldName = sortingFieldNameList.value, ascending = sortingOrderList.value, sd, sdNew; if (!fieldName) { return; } ascending = ascending === 'true'; sd = cvSorting.sortDescriptions; sdNew = new wijmo.collections.SortDescription(fieldName, ascending); // remove any old sort descriptors and add the new one sd.splice(0, sd.length, sdNew); };
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Filtering

The CollectionView class supports filtering through the ICollectionView interface, which is identical to the one in .NET. To enable filtering, set the CollectionView.filter property to a function that determines which objects to be included in the view. For the client side Filtering to work, the DisableServerRead property of the ItemSource should be true.

In this example, we create a filter for the country, and get the filter value from the input control. When you input the filter, the grid will be refreshed and render the fitlered data.

<div class="row-fluid well"> <input id="filteringInput" type="text" class="form-control app-pad" placeholder="Please input the character you want filter by country(case-insensitive)" /> </div> @(Html.C1().FlexGrid().Id("filteringGrid").IsReadOnly(true).AllowSorting(false).AutoGenerateColumns(false) .Bind(b => b.Bind(Model.Customers).DisableServerRead(true)) .Columns(columns => columns .Add(c => c.Binding("CustomerID")) .Add(c => c.Binding("CompanyName")) .Add(c => c.Binding("ContactName")) .Add(c => c.Binding("City")) .Add(c => c.Binding("Country")) .Add(c => c.Binding("Phone")) ) )
function getFilterNames() { return ['ProductID', 'ProductName', 'SupplierID', 'CategoryID', 'QuantityPerUnit', 'UnitPrice', 'UnitsInStock', 'UnitsOnOrder', 'ReorderLevel']; }; $(document).ready(function () { //Filtering // create collectionview, grid, filter with timeout, textbox for inputting filter. filteringGrid = wijmo.Control.getControl('#filteringGrid'); cvFiltering = filteringGrid.itemsSource; filteringInput = document.getElementById('filteringInput'); // apply filter when input filteringInput.addEventListener('input', filterGrid); }); //Filtering // create collectionview, grid, filter with timeout, textbox for inputting filter. var cvFiltering = null, filteringGrid = null, toFilter, filteringInput = null; // define the filter function for the collection view. function filterFunction(item) { var filter = filteringInput.value.toLowerCase(); if (!filter) { return true; } return item.CustomerID.toLowerCase().indexOf(filter) > -1; }; // apply filter (applied on a 500 ms timeOut) function filterGrid() { if (toFilter) { clearTimeout(toFilter); } toFilter = setTimeout(function () { toFilter = null; if (cvFiltering.filter === filterFunction) { cvFiltering.refresh(); } else { cvFiltering.filter = filterFunction; } }, 500); };
using System.Web.Mvc; using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Grouping

The CollectionView class supports grouping through the ICollectionView interface, which is identical to the one in .NET. To enable grouping, add one or more GroupDescription objects to the CollectionView.groupDescriptions property, and ensure that the grid's showGroups property is set to true when creating the grid instance(the default value is false.).

GroupDescription objects are flexible, allowing you to group data based on value or on grouping functions.

The example below groups the collection by the field which you select from the list. The grid shows not only the items content but also the group information: the group name and the average value of amount in the group. You can find the rendering codes for these in the method initTBody. The corresponding code snippet locates in line 116.

Notes: Selecting one item in the list will add a new instance of GroupDescription. If the groupdescription already exists, nothing happens. In order to clear the group setting, select the first item in the list.

<div class="row-fluid well"> <select id="groupingFieldNameList" class="form-control"></select> </div> @(Html.C1().FlexGrid().Id("groupingGrid").IsReadOnly(true).AllowSorting(true).AutoGenerateColumns(true) .Bind(b => b.DisableServerRead(true).Bind(Model.Products)) )
$(document).ready(function () { //Grouping // reference collectionview, grid, the select element and the names list. groupingGrid = wijmo.Control.getControl('#groupingGrid'); cvGrouping = groupingGrid.itemsSource; groupingFieldNameList = document.getElementById('groupingFieldNameList'); groupingFieldNameList.addEventListener('change', groupGrid); // initialize the list and listen to the list's change. groupingFieldNameList.innerHTML += ''; for (var i = 0; i < groupingNames.length; i++) { groupingFieldNameList.innerHTML += ''; } }); //Grouping // create collectionview, grid, the select element and the names list. var cvGrouping = null, groupingGrid = null, groupingFieldNameList = null, groupingNames = getFilterNames(); // update the group settings. function groupGrid() { var gd, fieldName = groupingFieldNameList.value; gd = cvGrouping.groupDescriptions; if (!fieldName) { // clear all the group settings. gd.splice(0, gd.length); return; } if (findGroup(fieldName) >= 0) { return; } if (fieldName === 'UnitPrice') { // when grouping by amount, use ranges instead of specific values gd.push(new wijmo.collections.PropertyGroupDescription(fieldName, function (item, propName) { var value = item[propName]; // UnitPrice if (value > 100) return 'Large Amounts'; if (value > 50) return 'Medium Amounts'; if (value > 0) return 'Small Amounts'; return 'Negative Amounts'; })); } else { // group by specific property values gd.push(new wijmo.collections.PropertyGroupDescription(fieldName)); } }; // check whether the group with the specified property name already exists. function findGroup(propName) { var gd = cvGrouping.groupDescriptions; for (var i = 0; i < gd.length; i++) { if (gd[i].propertyName === propName) { return i; } } return -1; };
using System.Web.Mvc; using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Tracking changes

The CollectionView class can keep track of changes made to the data. It is useful in situations where you must submit changes to the server. To turn on change tracking, set the trackChanges property to true. Once you do that, the CollectionView keeps track of any changes made to the data and exposes them in three arrays:

This feature is demonstrated below using a FlexGrid. The grid is bound to a CollectionView with trackChanges set to true.

<h5>Change the data here</h5> @(Html.C1().FlexGrid().Id("tcMainGrid").AutoGenerateColumns(false) .AllowAddNew(true).AllowDelete(true) .Columns(columns => columns .Add(c => c.Binding("CategoryID")) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) .Bind(ib => ib.Bind(Model.Categories).DisableServerRead(true) ) ) <h5>See the changes here</h5> <h6>Items edited:</h6> @(Html.C1().FlexGrid().Id("tcEditedGrid").AutoGenerateColumns(false) .IsReadOnly(true).Height(100) .Columns(columns => columns .Add(c => c.Binding("CategoryID").IsReadOnly(true)) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) ) <h6>Items added:</h6> @(Html.C1().FlexGrid().Id("tcAddedGrid").AutoGenerateColumns(false) .IsReadOnly(true).Height(100) .Columns(columns => columns .Add(c => c.Binding("CategoryID").IsReadOnly(true)) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) ) <h6>Items removed:</h6> @(Html.C1().FlexGrid().Id("tcRemovedGrid").AutoGenerateColumns(false) .IsReadOnly(true).Height(100) .Columns(columns => columns .Add(c => c.Binding("CategoryID").IsReadOnly(true)) .Add(c => c.Binding("CategoryName")) .Add(c => c.Binding("Description").Width("*"))) )
$(document).ready(function () { //Tracking changes tcMainGrid = wijmo.Control.getControl('#tcMainGrid');// the flexGrid to edit the data tcEditedGrid = wijmo.Control.getControl('#tcEditedGrid'); // the flexGrid to record the edited items tcAddedGrid = wijmo.Control.getControl('#tcAddedGrid'); // the flexGrid to record the added items tcRemovedGrid = wijmo.Control.getControl('#tcRemovedGrid'); // the flexGrid to record the removed items cvTrackingChanges = tcMainGrid.itemsSource; tcEditedGrid.itemsSource = cvTrackingChanges.itemsEdited; tcAddedGrid.itemsSource = cvTrackingChanges.itemsAdded; tcRemovedGrid.itemsSource = cvTrackingChanges.itemsRemoved; // track changes of the collectionview cvTrackingChanges.trackChanges = true; }); //Tracking changes var tcMainGrid = null, tcEditedGrid = null, tcAddedGrid = null, tcRemovedGrid = null, cvTrackingChanges = null;
using System.Web.Mvc; using ASPNetMVCCollectionView101.Models; using C1.Web.Mvc.Grid; using C1.Web.Mvc; using C1.Web.Mvc.Serializition; using System.Data.Entity.Validation; using System.Data.Entity; namespace ASPNetMVCCollectionView101.Controllers { public class HomeController : Controller { private C1NWindEntities db = new C1NWindEntities(); // GET: Home public ActionResult Index() { return View(db); } } }

Result (live):

Change the data here
See the changes here
Items edited:
Items added:
Items removed: