Features

Custom Editors

Custom Editors

Features

Description

This sample shows how you can use custom editors to change the values in MultiRow cells.

The sample uses the EditTemplateId property to specify the id of the template for cell editor. When the user starts editing a cell, the editor will show and get the focus.

There are three kinds of editors.

  • Use C1 Input controls which has "Value" or "Text" property.

    You only need set the EditTemplate property of a cell.

    If the grid allows adding a new row, you need set the editor's IsRequired to false. If you want the editor full with the whole cell, you can set the style width to "100%".

    In this sample, "Date", "Time", "Country", "Amount" and "Color" use C1 Input controls as the editors.

  • Use other control or a C1 control which has NO "Value" or "Text" property.

    You need listen the grid's OnClientCellEditEnding event and set

                         cellEditEndingEventArgs.cancel = true;
                    
    to apply your updating.

    Then you need consider when to update the cell value and remove the editor from a grid in your application. For example, when the editor blurs, we need update the cell value and remove the editor.

    In this sample, the "Product" column uses <input /> as the editor.

    To get the binding column in the event handler, please use the MultiRow's getBindingColumn function.

  • Use grid's internal editor.

    In this sample, "Amount2" and "Active" use grid internal editor. You don't need do anything.

using C1.Web.Mvc;
using MultiRowExplorer.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using C1.Web.Mvc.Serialization;

namespace MultiRowExplorer.Controllers
{
    public partial class MultiRowController : Controller
    {
        private static List<Sale> Source = Sale.GetData(10).ToList<Sale>();
        public ActionResult CustomEditors()
        {
            ViewBag.Countries = Sale.GetCountries();
            ViewBag.Products = Sale.GetProducts();
            return View();
        }

        public ActionResult CustomEditors_Bind([C1JsonRequest] CollectionViewRequest<Sale> requestData)
        {
            return this.C1Json(CollectionViewHelper.Read(requestData, Source));
        }

        public ActionResult MultiRowEditorsUpdate([C1JsonRequest]CollectionViewEditRequest<Sale> requestData)
        {
            return this.C1Json(CollectionViewHelper.Edit<Sale>(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 MultiRowEditorsCreate([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 MultiRowEditorsDelete([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('#customEditorsMultiRow');
            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 row = cellRangeEventArgs.row, col = cellRangeEventArgs.col;
            var bcol = grid.getBindingColumn(grid.cells, row, col);
            // when it is the editor of the "Product" column,
            // apply the updating manually.
            if (!cellRangeEventArgs.cancel
                && cellRangeEventArgs.panel.cellType == wijmo.grid.CellType.Cell
                && bcol.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('#customEditorsMultiRow');
                updateCellValue(grid, cellRange);
                wrapper.parentNode.removeChild(wrapper);
            });
        }

        // the id of the element would be [MultiRow's id] + "_Cell_r" + row + "_c" + col + "_"
        function getActiveEditorCellRange(ele) {
            var id = ele.id, strId,
                row, col, index;
            if (id) {
                strId = id.substr('customEditorsMultiRow_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;
            cv.editItem(cv.items[row]);
            var input = getEditorControl(row, col);
            grid.setCellData(row, col, input.value);
            cv.commitEdit();
            cv.commitNew();
            grid.select(sel);
        }

        function getEditorControl(row, col) {
            var elementId = 'customEditorsMultiRow_Cell_r' + row + '_c' + col;
            return document.getElementById(elementId);
        }
</script>
}

<!-- MultiRow hosting the custom editors -->
<c1-multi-row id="customEditorsMultiRow" allow-add-new="true" allow-delete="true"
              key-action-tab="Cycle" class="multirow" cell-edit-ending="cellEditEnding">
    <c1-items-source read-action-url="@Url.Action("CustomEditors_Bind")"
                     update-action-url="@Url.Action("MultiRowEditorsUpdate")"
                     create-action-url="@Url.Action("MultiRowEditorsCreate")"
                     delete-action-url="@Url.Action("MultiRowEditorsDelete")">
    </c1-items-source>
    <c1-multi-row-cell-group>
        <c1-multi-row-cell binding="ID" header="ID" is-read-only="true"></c1-multi-row-cell>
        <c1-multi-row-cell binding="Active" header="Active" is-read-only="false"></c1-multi-row-cell>
    </c1-multi-row-cell-group>
    <c1-multi-row-cell-group>
        <c1-multi-row-cell binding="Start" header="Date" width="150" format="d">
            <c1-flex-grid-cell-template is-editing="true">
                <c1-input-date id="dateEditor" style="width:100%" is-required="false" format="d" template-bindings="@(new{ Value = "Start"})"></c1-input-date>
            </c1-flex-grid-cell-template>
        </c1-multi-row-cell>
        <c1-multi-row-cell binding="End" header="Time" format="t">
            <c1-flex-grid-cell-template is-editing="true">
                <c1-input-time id="timeEditor" style="width:100%" is-required="false" step="30" format="t" template-bindings="@(new {Value="End"})"></c1-input-time>
            </c1-flex-grid-cell-template>
        </c1-multi-row-cell>
    </c1-multi-row-cell-group>
    <c1-multi-row-cell-group colspan="2">
        <c1-multi-row-cell binding="Country" header="Country" colspan="2">
            <c1-flex-grid-cell-template is-editing="true">
                <c1-combo-box id="countryEditor" style="width:100%" is-editable="false" template-bindings="@(new {Text="Country"})">
                    <c1-items-source source-collection="@countries"></c1-items-source>
                </c1-combo-box>
            </c1-flex-grid-cell-template>
        </c1-multi-row-cell>
        <c1-multi-row-cell binding="Product" header="Product">
            <c1-flex-grid-cell-template is-editing="true">
                <input type="text" id="{{uid}}" onfocus="productEditorFocus(event)" onblur="productEditorBlur(event)" style="width:100%;height:100%" value="{{Product}}" />
            </c1-flex-grid-cell-template>
        </c1-multi-row-cell>
        <c1-multi-row-cell binding="Color" header="Color">
            <c1-flex-grid-cell-template is-editing="true">
                <c1-input-color id="colorEditor" style="width:100%" is-required="false" template-bindings="@(new {Value="Color"})"></c1-input-color>
            </c1-flex-grid-cell-template>
        </c1-multi-row-cell>
    </c1-multi-row-cell-group>
    <c1-multi-row-cell-group>
        <c1-multi-row-cell binding="Amount" header="Amount" format="n2">
            <c1-flex-grid-cell-template is-editing="true">
                <c1-input-number id="amountEditor" style="width:100%" is-required="false" format="c2" step="10" template-bindings="@(new {Value="Amount"})"></c1-input-number>
            </c1-flex-grid-cell-template>
        </c1-multi-row-cell>
        <c1-multi-row-cell binding="Amount2" header="Amount2"></c1-multi-row-cell>
    </c1-multi-row-cell-group>
</c1-multi-row>

@section Description{
    <p>
        This sample shows how you can use custom editors to change the values in <b>MultiRow</b> cells.
    </p>
    <p>
        The sample uses the <b>EditTemplateId</b> property to specify the id of the template for cell editor.
        When the user starts editing a cell, the editor will show and get the focus.
    </p>
    <p>
        There are three kinds of editors.
    </p>
    <ul class="normal">
        <li>
            <b>Use C1 Input controls which has "Value" or "Text" property.</b>
            <p>
                You only need set the <b>EditTemplate</b> property of a cell.
            </p>
            <p>
                If the grid allows adding a new row, you need set the editor's <b>IsRequired</b> to false.
                If you want the editor full with the whole cell, you can set the style width to "100%".
            </p>
            <p>
                In this sample, "Date", "Time", "Country", "Amount" and "Color" use C1 Input controls as the editors.
            </p>
        </li>
        <li>
            <b>Use other control or a C1 control which has NO "Value" or "Text" property.</b>
            <p>
                You need listen the grid's <b>OnClientCellEditEnding</b> event and set
                <pre>
                     cellEditEndingEventArgs.cancel = true;
                </pre>
                to apply your updating.
            </p>
            <p>
                Then you need consider when to update the cell value and remove the editor from a grid in your application.
                For example, when the editor blurs, we need update the cell value and remove the editor.
            </p>
            <p>
                In this sample, the "Product" column uses &lt;input /&gt; as the editor.
            </p>
            <p>
                To get the binding column in the event handler, please use the MultiRow's <b>getBindingColumn</b> function.
            </p>
        </li>
        <li>
            <b>Use grid's internal editor.</b>
            <p>
                In this sample, "Amount2" and "Active" use grid internal editor. You don't need do anything.
            </p>
        </li>
    </ul>
}