FlexGrid
Custom Editors
This sample shows how you can use custom editors to change the values in FlexGrid cells.
Features
Description
The sample demonstrates two ways to set up editors other than FlexGrid’s internal editor: EditTemplate and Column Editor properties.
When the user starts editing a cell, the editor will show and get the focus.
There can be two kinds of custom editors:
-
C1 Input controls which have "Value" or "Text" property:
To use EditTemplate, you need to declare the C1 Input control as template then assign it's id to the EditTemplateId property of a column or declare the control under the c1-flex-grid-cell-template tag.
The second way is to declare a C1 Input control as usual and then assign it's id to the Editor property of a column.
If the grid allows adding a new row, you need set the editor's IsRequired property to false. If you want to the editor to cover full width of the whole cell, you can set the style width to "100%".
This sample uses Editor property for "Start", "Country", "Color" and uses EditTemplate for "End", "Amount" to set C1 Input controls as the editors.
-
Use a control or a C1 control that does NOT have a ”Value" or "Text" property:
In this type of editors, you need to listen to the grid's OnClientCellEditEnding event and set
cellEditEndingEventArgs.cancel = true;
to apply updates.
Then you need to consider when to update the cell value and remove the editor from the grid in your application. For example, when the editor blurs, we need to update the cell value and remove the editor.
In this sample, the "Product" column uses <input /> as the editor to show this kind of editor.
using C1.Web.Mvc; using MvcExplorer.Models; using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using C1.Web.Mvc.Serialization; namespace MvcExplorer.Controllers { public partial class FlexGridController : Controller { private static List<Sale> Source = Sale.GetData(20).ToList<Sale>(); public ActionResult CustomEditors() { ViewBag.Countries = Sale.GetCountries(); ViewBag.Products = Sale.GetProducts(); return View(Source); } public ActionResult GridEditorsUpdate([C1JsonRequest]CollectionViewEditRequest<Sale> requestData) { return this.C1Json(CollectionViewHelper.Edit(requestData, sale => { string error = string.Empty; bool success = true; var fSale = Source.Find(item => item.ID == sale.ID); fSale.Country = sale.Country; fSale.Amount = sale.Amount; fSale.Start = sale.Start; fSale.End = sale.End; fSale.Product = sale.Product; fSale.Active = sale.Active; fSale.Amount2 = sale.Amount2; fSale.Color = sale.Color; return new CollectionViewItemResult<Sale> { Error = error, Success = success && ModelState.IsValid, Data = fSale }; }, () => Source)); } public ActionResult GridEditorsCreate([C1JsonRequest]CollectionViewEditRequest<Sale> requestData) { return this.C1Json(CollectionViewHelper.Edit(requestData, item => { string error = string.Empty; bool success = true; try { Source.Add(item); item.ID = Source.Max(u => u.ID) + 1; } catch (Exception e) { error = e.Message; success = false; } return new CollectionViewItemResult<Sale> { Error = error, Success = success, Data = item }; }, () => Source)); } public ActionResult GridEditorsDelete([C1JsonRequest]CollectionViewEditRequest<Sale> requestData) { return this.C1Json(CollectionViewHelper.Edit(requestData, item => { string error = string.Empty; bool success = true; try { var resultItem = Source.Find(u => u.ID == item.ID); Source.Remove(resultItem); } catch (Exception e) { error = e.Message; success = false; } return new CollectionViewItemResult<Sale> { Error = error, Success = success, Data = item }; }, () => Source)); } } }
@model IEnumerable<Sale> @{ List<string> countries = ViewBag.Countries; List<string> products = ViewBag.Products; } @section Scripts{ <script type="text/javascript"> c1.documentReady(function () { var grid = wijmo.Control.getControl('#customGridEditorsGrid'); grid.hostElement.addEventListener('keydown', function (e) { if (e.keyCode == 32) { e.preventDefault(); } }); }); // The following scripts added for customizing the updating for the Product column. // apply the customized updating for the Product column function cellEditEnding(grid, cellRangeEventArgs) { var col = cellRangeEventArgs.col; // when it is the editor of the "Product" column, // apply the updating manually. if (!cellRangeEventArgs.cancel && cellRangeEventArgs.panel.cellType == wijmo.grid.CellType.Cell && grid.columns[col].binding == 'Product') { updateCellValue(grid, cellRangeEventArgs); cellRangeEventArgs.cancel = true; } } function productEditorFocus(event) { var input = event.currentTarget; if (input) { wijmo.setSelectionRange(input, 0, input.value.length); } } function productEditorBlur(event) { var input = event.currentTarget; setTimeout(function () { if (wijmo.contains(document.activeElement, input) // ensure the input element is not removed from the page. || !document.body.contains(input)) { return; } var wrapper = input.parentNode, cellRange = getActiveEditorCellRange(input), grid = wijmo.Control.getControl('#customGridEditorsGrid'); updateCellValue(grid, cellRange); wrapper.parentNode.removeChild(wrapper); }); } // the id of the element would be [grid's id] + "_Cell_r" + row + "_c" + col + "_" function getActiveEditorCellRange(ele) { var id = ele.id, strId, row, col, index; if (id) { strId = id.substr('customGridEditorsGrid_Cell_r'.length); index = strId.indexOf('_c'); row = parseInt(strId.substring(0, index)); col = parseInt(strId.substr(index + 2)); return { row: row, col: col }; } } // update the Product cell's value with its editor. function updateCellValue(grid, cellRangeEventArgs) { var row, col, sel = grid.selection, cv = grid.collectionView; row = cellRangeEventArgs.row; col = cellRangeEventArgs.col; var input = getEditorControl(row, col); if (input == null) return; cv.editItem(cv.items[row]); grid.setCellData(row, col, input.value); cv.commitEdit(); cv.commitNew(); //grid.select(sel); } function getEditorControl(row, col) { var elementId = 'customGridEditorsGrid_Cell_r' + row + '_c' + col; return document.getElementById(elementId); } </script> } <script id="edtDate" type="text/template"> @(Html.C1().InputDate() .Id("dateEditor") .Format("d") .IsRequired(false) // add this for new row .CssStyle("width", "100%") // full with the cell .TemplateBind("Value", "Start") .ToTemplate() ) </script> <script id="edtTime" type="text/template"> @(Html.C1().InputTime() .Id("timeEditor") .Step(30) .Format("t") .IsRequired(false) // add this for new row .CssStyle("width", "100%") // full with the cell .TemplateBind("Value", "End").ToTemplate() ) </script> <script id="edtAmount" type="text/template"> @(Html.C1().InputNumber() .Id("amountEditor") .Format("c2") .IsRequired(false) // add this for new row .CssStyle("width", "100%") // full with the cell .Step(10) .TemplateBind("Value", "Amount").ToTemplate() ) </script> <script id="edtCountry" type="text/template"> @(Html.C1().ComboBox() .Id("countryEditor") .IsEditable(false) .Bind(countries) .CssStyle("width", "100%") // full with the cell .TemplateBind("Text", "Country").ToTemplate() ) </script> <script id="edtProduct" type="text/template"> <input type="text" id="{{uid}}" onfocus="productEditorFocus(event)" onblur="productEditorBlur(event)" style="width:100%;height:100%" value="{{Product}}" /> </script> <script id="edtColor" type="text/template"> @(Html.C1().InputColor() .Id("colorEditor") .CssStyle("width", "100%") // full with the cell .TemplateBind("Text", "Color").ToTemplate() ) </script> @(Html.C1().InputDate() .Id("dateEditor1") .Format("d") .IsRequired(false) // add this for new row .CssStyle("width", "100%") // full with the cell ) @(Html.C1().ComboBox() .Id("countryEditor1") .IsEditable(false) .Bind(countries) .CssStyle("width", "100%") // full with the cell ) @(Html.C1().InputColor() .Id("colorEditor1") .CssStyle("width", "100%") // full with the cell ) <!-- FlexGrid hosting the custom editors --> @(Html.C1().FlexGrid<Sale>() .Id("customGridEditorsGrid") .KeyActionTab(C1.Web.Mvc.Grid.KeyAction.Cycle) .AllowAddNew(true) .AllowDelete(true) .AutoGenerateColumns(false) .Columns(bl => { bl.Add(cb => cb.Binding("ID").Width("0.4*").IsReadOnly(true)); bl.Add(cb => cb.Binding("Start").Header("Date").Width("*").Format("d").Editor("dateEditor1")); bl.Add(cb => cb.Binding("End").Header("Time").Width("*").Format("t").CellTemplate(ctb => ctb.EditTemplateId("edtTime"))); bl.Add(cb => cb.Binding("Country").Width("1.5*").Editor("countryEditor1")); bl.Add(cb => cb.Binding("Product").Width("1.5*").CellTemplate(ctb => ctb.EditTemplateId("edtProduct"))); bl.Add(cb => cb.Binding("Amount").Format("n2").Width("1.5*").CellTemplate(ctb => ctb.EditTemplateId("edtAmount"))); bl.Add(cb => cb.Binding("Amount2").Format("n2").Width("1.5*")); bl.Add(cb => cb.Binding("Color").Width("1.5*").Editor("colorEditor1")); bl.Add(cb => cb.Binding("Active").Width("1.5*")); }) .Bind(bl => bl.Update(Url.Action("GridEditorsUpdate")) .Create(Url.Action("GridEditorsCreate")) .Delete(Url.Action("GridEditorsDelete")) .Bind(Model)) .CssStyle("height", "500px") .OnClientCellEditEnding("cellEditEnding") ) @section Summary{ <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text0)</p> } @section Description{ <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text1)</p> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text2)</p> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text3)</p> <ul class="normal"> <li> <b>@Html.Raw(Resources.FlexGrid.CustomEditors_Text4)</b> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text5)</p> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text6)</p> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text7)</p> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text8)</p> </li> <li> <b>@Html.Raw(Resources.FlexGrid.CustomEditors_Text9)</b> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text10)</p> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text11)</p> <p>@Html.Raw(Resources.FlexGrid.CustomEditors_Text12)</p> </li> </ul> }