Custom Editors
Although FlexGrid provides efficient, Excel-style editing by default, you may want to customize the editing behavior.
This example defines a CustomGridEditor class that allows any Wijmo control to be used as a grid editor:
- C1FlexGrid/CustomEditors.js
- C1FlexGrid/CustomEditors.css
- CustomEditorsController.cs
- CustomEditors.cshtml
// This file locates: "Scripts/Lesson/C1FlexGrid/CustomEditors.js". c1.documentReady(function () { // create some random data var countries = 'US,Germany,UK,Japan,Italy,Greece'.split(','); var products = [ { id: 0, name: 'Widget', unitPrice: 23.43 }, { id: 1, name: 'Gadget', unitPrice: 12.33 }, { id: 2, name: 'Doohickey', unitPrice: 53.07 }, ]; var data = []; var dt = new Date(); for (var i = 0; i < 100; i++) { data.push({ id: i, date: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60), time: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60), country: countries[Math.floor(Math.random() * countries.length)], product: products[Math.floor(Math.random() * products.length)].name, amount: Math.random() * 10000 - 5000, discount: Math.random() / 4 }); } var theGrid = wijmo.Control.getControl('#theGrid'); theGrid.itemsSource = data; // add custom editors to the grid new CustomGridEditor(theGrid, 'date', wijmo.input.InputDate, { format: 'd' }); new CustomGridEditor(theGrid, 'time', wijmo.input.InputTime, { format: 't', min: new Date(2000, 1, 1, 7, 0), max: new Date(2000, 1, 1, 22, 0), step: 30 }); new CustomGridEditor(theGrid, 'country', wijmo.input.ComboBox, { itemsSource: countries }); new CustomGridEditor(theGrid, 'amount', wijmo.input.InputNumber, { format: 'n2', step: 10 }); // create an editor based on a ComboBox var multiColumnEditor = new CustomGridEditor(theGrid, 'product', wijmo.input.ComboBox, { headerPath: 'name', displayMemberPath: 'name', itemsSource: products }); // customize the ComboBox to show multiple columns var combo = multiColumnEditor.control; combo.listBox.formatItem.addHandler(function (s, e) { e.item.innerHTML = '<table><tr>' + '<td style="width:30px;text-align:right;padding-right:6px">' + e.data.id + '</td>' + '<td style="width:100px;padding-right:6px"><b>' + e.data.name + '</b></td>' + '<td style="width:80px;text-align:right;padding-right:6px">' + wijmo.Globalize.format(e.data.unitPrice, 'c') + '</td>' + '</tr></table>'; }); }); // *** CustomGridEditor class (transpiled from TypeScript) *** var CustomGridEditor = (function () { /** * Initializes a new instance of a CustomGridEditor. */ function CustomGridEditor(flex, binding, edtClass, options) { var _this = this; // save references this._grid = flex; this._col = flex.columns.getColumn(binding); // create editor this._ctl = new edtClass(document.createElement('div'), options); // connect grid events this._grid.beginningEdit.addHandler(this._beginningEdit, this); this._grid.scrollPositionChanged.addHandler(this._closeEditor, this); // connect editor events this._ctl.addEventListener(this._ctl.hostElement, 'keydown', function (e) { switch (e.keyCode) { case wijmo.Key.Tab: case wijmo.Key.Enter: _this._closeEditor(true); _this._grid.focus(); // forward event to the grid so it will move the selection var evt = document.createEvent('HTMLEvents'); evt.initEvent('keydown', true, true); evt['ctrlKey'] = e.ctrlKey; evt['shiftKey'] = e.shiftKey; evt['keyCode'] = e.keyCode; _this._grid.hostElement.dispatchEvent(evt); break; case wijmo.Key.Escape: _this._closeEditor(false); _this._grid.focus(); break; } }); this._ctl.lostFocus.addHandler(function () { setTimeout(function () { if (!_this._ctl.containsFocus()) { _this._closeEditor(true); } }); }); // open drop-down on f4/alt-down this._grid.addEventListener(this._grid.hostElement, 'keydown', function (e) { _this._openDropDown = false; if (e.keyCode == wijmo.Key.F4 || (e.altKey && (e.keyCode == wijmo.Key.Down || e.keyCode == wijmo.Key.Up))) { _this._openDropDown = true; _this._grid.startEditing(true); e.preventDefault(); } }, true); // close editor when user resizes the window window.addEventListener('resize', function () { if (_this._ctl.containsFocus()) { _this._closeEditor(true); _this._grid.focus(); } }); } Object.defineProperty(CustomGridEditor.prototype, "control", { // gets an instance of the control being hosted by this grid editor get: function () { return this._ctl; }, enumerable: true, configurable: true }); // handle the grid's beginningEdit event by canceling the built-in editor, // initializing the custom editor and giving it the focus. CustomGridEditor.prototype._beginningEdit = function (grid, args) { var _this = this; // check that this is our column if (grid.columns[args.col] != this._col) { return; } // check that this is not the Delete key // (which is used to clear cells and should not be messed with) var evt = args.data; if (evt && evt.keyCode == wijmo.Key.Delete) { return; } // cancel built-in editor args.cancel = true; // save cell being edited this._rng = args.range; // initialize editor host var rc = grid.getCellBoundingRect(args.row, args.col); wijmo.setCss(this._ctl.hostElement, { position: 'absolute', left: rc.left - 1 + pageXOffset, top: rc.top - 1 + pageYOffset, width: rc.width + 1, height: grid.rows[args.row].renderHeight + 1, borderRadius: '0px' }); // initialize editor content if (!wijmo.isUndefined(this._ctl['text'])) { this._ctl['text'] = grid.getCellData(this._rng.row, this._rng.col, true); } else { throw 'Can\'t set editor value/text...'; } // start editing item var ecv = wijmo.tryCast(grid.collectionView, 'IEditableCollectionView'), item = grid.rows[args.row].dataItem; if (ecv && item) { setTimeout(function () { ecv.editItem(item); }, 210); // FlexGrid commits edits 200ms after losing focus } // activate editor document.body.appendChild(this._ctl.hostElement); this._ctl.focus(); setTimeout(function () { // get the key that triggered the editor var key = (evt && evt.charCode > 32) ? String.fromCharCode(evt.charCode) : null; // send key to editor if (key) { var input = _this._ctl.hostElement.querySelector('input'); if (input instanceof HTMLInputElement) { input.value = key; wijmo.setSelectionRange(input, key.length, key.length); var evtInput = document.createEvent('HTMLEvents'); evtInput.initEvent('input', true, false); input.dispatchEvent(evtInput); } } else { _this._ctl.focus(); } // open drop-down on F4/alt-down if (_this._openDropDown && _this._ctl instanceof wijmo.input.DropDown) { _this._ctl.isDroppedDown = true; } }, 50); }; // close the custom editor, optionally saving the edits back to the grid CustomGridEditor.prototype._closeEditor = function (saveEdits) { if (this._rng) { var grid = this._grid, ctl = this._ctl, host = ctl.hostElement; // raise grid's cellEditEnding event var e = new wijmo.grid.CellEditEndingEventArgs(grid.cells, this._rng); grid.onCellEditEnding(e); // save editor value into grid if (saveEdits) { if (!wijmo.isUndefined(ctl['value'])) { this._grid.setCellData(this._rng.row, this._rng.col, ctl['value']); } else if (!wijmo.isUndefined(ctl['text'])) { this._grid.setCellData(this._rng.row, this._rng.col, ctl['text']); } else { throw 'Can\'t get editor value/text...'; } this._grid.invalidate(); } // close editor and remove it from the DOM if (ctl instanceof wijmo.input.DropDown) { ctl.isDroppedDown = false; } host.parentElement.removeChild(host); this._rng = null; // raise grid's cellEditEnded event grid.onCellEditEnded(e); } }; return CustomGridEditor; }());
// This file locates: "Content/css/Lesson/C1FlexGrid/CustomEditors.css". .wj-flexgrid { max-height: 200px; margin-bottom: 12px; } .modal-content { width: 400px; } /* style our custom 'edit buttons' */ .wj-rowheaders .wj-cell .wj-glyph-pencil { opacity: .3; transform: scale(.5); } .wj-rowheaders .wj-cell:hover .wj-glyph-pencil { opacity: 1; color: #000080; transform: scale(1); transition: all linear 300ms; } /* wj-labeled-input: adapted from MDL styles */ /* label input container */ .wj-labeled-input { position: relative; font-size: 16px; display: inline-block; box-sizing: border-box; width: 300px; max-width: 100%; margin: 0 20px; padding: 20px 0; } /* Wijmo control in the container */ .wj-labeled-input .wj-control.wj-content { margin: 0; width: 100%; background-color: transparent; border: none; border-bottom: 1px solid rgba(0,0,0,.1); } .wj-labeled-input .wj-control.wj-content button { opacity: 0.75; border-color: rgba(0,0,0,.1); } .wj-labeled-input .wj-input-group .wj-form-control { float: none; } /* label in the container (must come after the control) */ .wj-labeled-input label { font-size: 16px; top: 24px; bottom: 0; margin: 0; pointer-events: none; position: absolute; display: block; width: 100%; overflow: hidden; white-space: nowrap; text-align: left; color: rgba(0, 0, 0, 0.258824); transition-duration: .2s; transition-timing-function: cubic-bezier(.4,0,.2,1); } /* move label out of the way when control is focused or not empty */ .wj-static-labels .wj-labeled-input :not(.wj-state-focused) + label, .wj-labeled-input .wj-state-focused + label, .wj-labeled-input :not(.wj-state-empty) + label { font-size: 12px; top: 4px; color: rgb(63,81,181); visibility: visible; } .wj-labeled-input .wj-state-invalid + label { color: #d50000; } /* underline label */ .wj-labeled-input label:after { background-color: rgb(63,81,181); bottom: 20px; content: ''; height: 2px; left: 45%; position: absolute; transition-duration: .2s; transition-timing-function: cubic-bezier(.4,0,.2,1); visibility: hidden; width: 10px; } /* show underline when focused */ .wj-labeled-input .wj-state-focused + label:after { left: 0; visibility: visible; width: 100%; } .wj-labeled-input .wj-state-invalid + label:after { background-color: #d50000; } /* validation message */ .wj-labeled-input .wj-error { color: #d50000; position: absolute; font-size: 12px; margin-top: 3px; visibility: hidden; display: block; } .wj-labeled-input .wj-control.wj-state-invalid ~ .wj-error { visibility: visible; }
using System.Web.Mvc; namespace LearnMvcClient.Controllers { public partial class C1FlexGridController : Controller { // GET: CustomEditors public ActionResult CustomEditors() { return View(); } } }
<h1> @Html.Raw(Resources.C1FlexGrid.CustomEditors_Title) </h1> <p> @Html.Raw(Resources.C1FlexGrid.CustomEditors_Text1) </p> <p> @Html.Raw(Resources.C1FlexGrid.CustomEditors_Text2) </p> @(Html.C1().FlexGrid().Id("theGrid") .AutoGenerateColumns(false) .Columns(cs=> { cs.Add().Binding("id").Header("ID").Width("40").IsReadOnly(true); cs.Add().Binding("date").Header("Date").Format("d"); cs.Add().Binding("time").Header("Time").Format("t"); cs.Add().Binding("country").Header("Country"); cs.Add().Binding("product").Header("Product"); cs.Add().Binding("amount").Header("Amount").Format("n2"); }) )