diff --git a/_contentTemplates/common/general-info.md b/_contentTemplates/common/general-info.md index 3ad9a5f5e0..0e7af72811 100644 --- a/_contentTemplates/common/general-info.md +++ b/_contentTemplates/common/general-info.md @@ -79,7 +79,7 @@ If you set the `ShouldRender` field of the event arguments to `true`, the compon | tb-gantt-editable | [Gantt with Editing](slug:gantt-tree-editing) | | tb-ganttcolumn | [Gantt - Databound Column](slug:gantt-columns-bound) | | tb-grid | [Grid](slug:grid-overview) | -| tb-grid-editable | [Grid with Editing](slug:components/grid/editing/overview) | +| tb-grid-editable | [Grid with Editing](slug:grid-editing-overview) | | tb-gridcolumn | [Grid - Databound Column](slug:components/grid/columns/bound) | | tb-gridcolumn-locked | [Grid with Frozen Column](slug:grid-columns-frozen) | | tb-gridlayout | [GridLayout](slug:gridlayout-overview) | diff --git a/_contentTemplates/common/grid-treelist-editing-notes.md b/_contentTemplates/common/grid-treelist-editing-notes.md deleted file mode 100644 index ebc72c5096..0000000000 --- a/_contentTemplates/common/grid-treelist-editing-notes.md +++ /dev/null @@ -1,10 +0,0 @@ -#grid-treelist-data-operations-while-editing - - * For operations like Filter, Group, Sort, Page, Search, Select, Row drag and Delete: - * InCell edit - if the validation is satisfied, a save operation will be executed. If the validation is **not** satisfied, editing will be cancelled, the `OnCancel` event will fire and the new user operation will be executed. In this case, you can handle the OnCancel event and set the `IsCancelled` property of the `CommandEventArgs` to true. Thus, the other data operation will be aborted and the Grid will remain in edit mode. - * Inline edit - regardless of the validation, editing will be cancelled, the `OnCancel` event will fire and the new user operation will be executed. In this case, you can handle the OnCancel event and set the `IsCancelled` property of the `CommandEventArgs` to true. Thus, the other data operation will be aborted and the Grid will remain in edit mode. - - * For operations like Edit, Add, Save: - * InCell edit - if the validation is satisfied, the currently edited item will be saved and the command will be executed. If the validation is **not** satisfied, the command will be blocked until the item is valid or editing is cancelled. - * Inline edit - if the validation is satisfied, `OnCancel` will be fired for the currently edited item and the command will be executed. If the validation is **not** satisfied, the command will be blocked until the item is valid or editing is cancelled. -#end \ No newline at end of file diff --git a/_contentTemplates/common/parameters-table-styles.md b/_contentTemplates/common/parameters-table-styles.md index f314fc28ec..b9050859bb 100644 --- a/_contentTemplates/common/parameters-table-styles.md +++ b/_contentTemplates/common/parameters-table-styles.md @@ -1,6 +1,6 @@ #table-layout @code { - TelerikTreeList TreeListRef { get; set; } = new TelerikTreeList(); + private IEnumerable? TreeListData { get; set; } - public string Result { get; set; } + private int TreeListPageSize { get; set; } = 5; - // Note: This can cause a performance delay if you do long operations here - // Note 2: The TreeList does not await this event, its purpose is to notify you of changes - // so you must not perform async operations and data loading here, or issues with the TreeList state may occur - // or other things you change on the page won't actually change. The .SetStateAsync() call redraws only the TreeList, but not the rest of the page - async Task OnStateChangedHandler(TreeListStateEventArgs args) - { - string changedSetting = args.PropertyName; - - if (changedSetting == "SortDescriptors") - { - foreach (var item in args.TreeListState.SortDescriptors) - { - Result = $"The {item.Member} field was sorted"; - } - } - else if (changedSetting == "FilterDescriptors") - { - // ensure certain state based on some condition - // in this example - ensure that the ID field is always filtered with a certain setting unless the user filters it explicitly - bool isIdFiltered = false; - - foreach (CompositeFilterDescriptor compositeFilterDescriptor in args.TreeListState.FilterDescriptors) - { - foreach(FilterDescriptor item in compositeFilterDescriptor.FilterDescriptors) - { - Result = $"The {item.Member} field was filtered"; - - // you could override a user action as well - change settings on the corresponding parameter - // make sure that the .SetStateAsync() method of the TeeList is always called if you do that - if (item.Member == "Name") - { - item.Value = "second level child 1 of 1 and 1"; - item.Operator = FilterOperator.Contains; - } - } - } - if (!isIdFiltered) - { - args.TreeListState.FilterDescriptors.Add(new FilterDescriptor - { - Member = "Id", - MemberType = typeof(int), - Operator = FilterOperator.IsLessThan, - Value = 10 - }); - } - //needed only if you will be overriding user actions or amending them - // if you only need to be notified of changes, you should not call this method - await TreeListRef.SetStateAsync(args.TreeListState); - } - } + private IEnumerable TreeListSelectedItems { get; set; } = new List(); - public List Data { get; set; } + private EmployeeService TreeListEmployeeService { get; set; } = new(); - // sample model + private int OnStateChangedCount { get; set; } - public class Employee - { - // hierarchical data collections - public List DirectReports { get; set; } - - // data fields for display - public int Id { get; set; } - public string Name { get; set; } - public string EmailAddress { get; set; } - public DateTime HireDate { get; set; } - } + private string TreeListStateChangedProperty { get; set; } = string.Empty; + private string TreeListStateChangedPropertyClass { get; set; } = string.Empty; - // data generation + private string TreeListStateString { get; set; } = string.Empty; - // used in this example for data generation and retrieval for CUD operations on the current view-model data - public int LastId { get; set; } = 1; + private bool _doubleStateChanged { get; set; } - protected override async Task OnInitializedAsync() - { - Data = await GetTreeListData(); - } + private List _operationsWithMultipleStateChanged = new List() { + "FilterDescriptors", + "SearchFilter" + }; - async Task> GetTreeListData() + private async Task OnTreeListStateChanged(TreeListStateEventArgs args) { - List data = new List(); - - for (int i = 1; i < 15; i++) + if (_doubleStateChanged) { - Employee root = new Employee - { - Id = LastId, - Name = $"root: {i}", - EmailAddress = $"{i}@example.com", - HireDate = DateTime.Now.AddYears(-i), - DirectReports = new List(), // prepare a collection for the child items, will be populated later in the code - }; - data.Add(root); - LastId++; - - for (int j = 1; j < 4; j++) - { - int currId = LastId; - Employee firstLevelChild = new Employee - { - Id = currId, - Name = $"first level child {j} of {i}", - EmailAddress = $"{currId}@example.com", - HireDate = DateTime.Now.AddDays(-currId), - DirectReports = new List(), // collection for child nodes - }; - root.DirectReports.Add(firstLevelChild); // populate the parent's collection - LastId++; - - for (int k = 1; k < 3; k++) - { - int nestedId = LastId; - // populate the parent's collection - firstLevelChild.DirectReports.Add(new Employee - { - Id = LastId, - Name = $"second level child {k} of {j} and {i}", - EmailAddress = $"{nestedId}@example.com", - HireDate = DateTime.Now.AddMinutes(-nestedId) - }); ; - LastId++; - } - } + _doubleStateChanged = false; + await Task.Delay(1500); + TreeListStateChangedPropertyClass = string.Empty; } - return await Task.FromResult(data); - } -} -```` - -### Initiate Editing or Inserting of an Item - -The TreeList state lets you store the item that the user is currently working on - both an existing model that is being edited, and a new item the user is inserting. This happens automatically when you save the TreeList state. If you want to save on every keystroke instead of on `OnChange` - use a custom editor template and update the `EditItem` or `InsertedItem` of the state object as required, then save the state into your service. - -In addition to that, you can also use the `EditItem`, `OriginalEditItem`, `InsertItem` and `ParentItem` fields of the state object to put the TreeList in edit/insert mode through your own application code, instead of needing the user to initiate this through a [command button](slug:treelist-columns-command). - ->caption Put and item in Edit mode or start Inserting a new item - -````RAZOR -@* This example shows how to make the grid edit a certain item or start insert operation - through your own code, without requiring the user to click the Command buttons. - The buttons that initiate these operations can be anywhere on the page, including inside the grid. - Note the model constructors and static method that show how to get a new instance for the edit item -*@ - -Edit item 2 -Insert Item -Insert Item as child of Item 3 - - - - - Add - - - - Add Child - Edit - Delete - Save - Cancel - - - - - - - - - -@code { - public List Data { get; set; } - TelerikTreeList TreeListRef { get; set; } = new TelerikTreeList(); - - async Task EnterEditMode() - { - var state = TreeListRef.GetState(); - - Employee originalEmployee = FindItemRecursive(Data, 2); - Employee employeeToEdit = Employee.GetClonedInstance(originalEmployee); - - state.EditItem = employeeToEdit; - state.OriginalEditItem = originalEmployee; - await TreeListRef.SetStateAsync(state); - } + ++OnStateChangedCount; - async Task InsertItem() - { - var state = TreeListRef.GetState(); - state.InsertedItem = new Employee() { Name = "added from code" }; - await TreeListRef.SetStateAsync(state); - } + TreeListStateChangedProperty = args.PropertyName; - async Task InsertItemAsSpecificChild() - { - var state = TreeListRef.GetState(); - state.InsertedItem = new Employee(); - state.ParentItem = FindItemRecursive(Data, 3); - await TreeListRef.SetStateAsync(state); - } + // serialize the TreeListState and highlight the changed property + TreeListStateString = JsonSerializer.Serialize(args.TreeListState, new JsonSerializerOptions() { WriteIndented = true }) + .Replace($"\"{TreeListStateChangedProperty}\"", $"\"{TreeListStateChangedProperty}\""); - // sample helper method for handling the view-model data hierarchy - Employee FindItemRecursive(List items, int id) - { - foreach (var item in items) + // highlight first TreeListStateChangedProperty during filtering, grouping and search + if (_operationsWithMultipleStateChanged.Contains(TreeListStateChangedProperty)) { - if (item.Id.Equals(id)) - { - return item; - } - - if (item.DirectReports?.Count > 0) - { - var childItem = FindItemRecursive(item.DirectReports, id); - - if (childItem != null) - { - return childItem; - } - } + _doubleStateChanged = true; + TreeListStateChangedPropertyClass = "first-of-two"; } - - return null; } - // Sample CUD operations for the local data - async Task UpdateItem(TreeListCommandEventArgs args) + private async Task OnTreeListCreate(TreeListCommandEventArgs args) { - var item = args.Item as Employee; + var createdItem = (Employee)args.Item; + var parentItem = (Employee?)args.ParentItem; - // perform actual data source operations here through your service - await MyService.Update(item); + await TreeListEmployeeService.Create(createdItem, parentItem); - // update the local view-model data with the service data - await GetTreeListData(); + TreeListData = await TreeListEmployeeService.Read(); } - async Task CreateItem(TreeListCommandEventArgs args) + private async Task OnTreeListUpdate(TreeListCommandEventArgs args) { - var item = args.Item as Employee; - var parentItem = args.ParentItem as Employee; + var updatedItem = (Employee)args.Item; - // perform actual data source operations here through your service - await MyService.Create(item, parentItem); + await TreeListEmployeeService.Update(updatedItem); - // update the local view-model data with the service data - await GetTreeListData(); + TreeListData = await TreeListEmployeeService.Read(); } - async Task DeleteItem(TreeListCommandEventArgs args) + protected override async Task OnInitializedAsync() { - var item = args.Item as Employee; - - // perform actual data source operations here through your service - await MyService.Delete(item); - - // update the local view-model data with the service data - await GetTreeListData(); + TreeListData = await TreeListEmployeeService.Read(); } +@[template](/_contentTemplates/treelist/editing.md#flat-crud-service-and-model) +} +```` - // sample model - - public class Employee - { - public int Id { get; set; } - - public string Name { get; set; } - public string EmailAddress { get; set; } - public DateTime HireDate { get; set; } - - public List DirectReports { get; set; } - public bool HasChildren { get; set; } +## Methods - // example of comparing stored items (from editing or selection) - // with items from the current data source - IDs are used instead of the default references - // Also used for the editing so replacing the object in the view-model data - // will treat it as the same object and keep its state - otherwise it will - // collapse after editing is done, which is not what the user would expect - public override bool Equals(object obj) - { - if (obj is Employee) - { - return this.Id == (obj as Employee).Id; - } - return false; - } +The `GetState` and `SetStateAsync` methods of the [TreeList instance](slug:treelist-overview#treelist-reference-and-methods) let you get and set the current TreeList state on demand at any time *after* [`OnStateInit`](#onstateinit). - // define constructors and a static method so we can deep clone instances - // we use that to define the edited item - otherwise the references will point - // to the item in the grid data sources and all changes will happen immediately on - // the Data collection, and we don't want that - so we need a deep clone with its own reference - // this is just one way to implement this, you can do it in a different way - public Employee() - { +* `GetState` returns the current TreeList state, so you can save it or [retrieve specific information](#information-in-the-treelist-state). For example, you can [use `GetState` to get the current filters, sorts, and page number](slug:grid-kb-get-filtered-data). Or, you can [get the current TreeList column properties like order index, width, and others)](slug:grid-kb-column-state). - } +* `SetStateAsync` receives an instance of a `TreeListState` object and applies it to the TreeList. For example, you can have a button that puts the TreeList in a certain configuration programmatically, for example sort or filter the data, enter or exit edit mode, expand or collapse rows, etc. - public Employee(Employee itmToClone) - { - this.Id = itmToClone.Id; - this.Name = itmToClone.Name; - this.EmailAddress = itmToClone.EmailAddress; - this.HireDate = itmToClone.HireDate; - this.DirectReports = itmToClone.DirectReports != null ? new List(itmToClone.DirectReports) : new List(); - this.HasChildren = itmToClone.HasChildren; - } +If you want to make changes to the current TreeList state: - public static Employee GetClonedInstance(Employee itmToClone) - { - return new Employee(itmToClone); - } - } +1. First, get the current state with the `GetState` method. +1. Apply the desired modifications to the obtained `TreeListState` object. +1. Set the modified state object via the `SetStateAsync` method. - // data generation +> Do not use `GetState()` in the [`OnStateInit`](#onstateinit) or [`OnStateChanged`](#onstatechanged) events. Do not use `SetStateAsync()` in `OnStateInit`. Instead, get or set the `TreeListState` property of the event argument. +> +> Avoid calling `SetStateAsync` in the TreeList [CRUD methods](slug:treelist-editing-overview) (such as `OnUpdate`, `OnEdit`, `OnCreate`, `OnCancel`). Doing so may lead to unexpected results because the TreeList has more logic to execute after these events. - async Task GetTreeListData() - { - Data = await MyService.Read(); - } +>tip To reset the TreeList state to its initial markup configuration, call `SetStateAsync(null)`. +> +> To reset the TreeList state to a completely new configuration, create a `new TreeListState()` and apply the settings there. Then pass the state object to `SetStateAsync()`. - protected override async Task OnInitializedAsync() - { - await GetTreeListData(); - } - // the following static class mimics an actual data service that handles the actual data source - // replace it with your actual service through the DI, this only mimics how the API can look like and works for this standalone page - public static class MyService - { - private static List _data { get; set; } = new List(); - // used in this example for data generation and retrieval for CUD operations on the current view-model data - private static int LastId { get; set; } = 1; +### SetStateAsync Examples - public static async Task Create(Employee itemToInsert, Employee parentItem) - { - InsertItemRecursive(_data, itemToInsert, parentItem); - } +The tabs below show how to set the TreeList state and control filtering, sorting and other TreeList features. - public static async Task> Read() - { - if (_data.Count < 1) - { - for (int i = 1; i < 15; i++) - { - Employee root = new Employee - { - Id = LastId, - Name = $"root: {i}", - EmailAddress = $"{i}@example.com", - HireDate = DateTime.Now.AddYears(-i), - DirectReports = new List(), - HasChildren = true - }; - _data.Add(root); - LastId++; - - for (int j = 1; j < 4; j++) - { - int currId = LastId; - Employee firstLevelChild = new Employee - { - Id = currId, - Name = $"first level child {j} of {i}", - EmailAddress = $"{currId}@example.com", - HireDate = DateTime.Now.AddDays(-currId), - DirectReports = new List(), - HasChildren = true - }; - root.DirectReports.Add(firstLevelChild); - LastId++; - - for (int k = 1; k < 3; k++) - { - int nestedId = LastId; - firstLevelChild.DirectReports.Add(new Employee - { - Id = LastId, - Name = $"second level child {k} of {j} and {i}", - EmailAddress = $"{nestedId}@example.com", - HireDate = DateTime.Now.AddMinutes(-nestedId) - }); ; - LastId++; - } - } - } - } +@[template](/_contentTemplates/treelist/state.md#initial-state) - return await Task.FromResult(_data); - } +
+````RAZOR Sorting +@[template](/_contentTemplates/treelist/state.md#set-sort-from-code) +```` +````RAZOR FilterRow +@[template](/_contentTemplates/treelist/state.md#filter-row-from-code) +```` +````RAZOR FilterMenu +@[template](/_contentTemplates/treelist/state.md#filter-menu-from-code) +```` +````RAZOR Search +@[template](/_contentTemplates/treelist/state.md#search-from-code) +```` +````RAZOR ExpandedItems +@[template](/_contentTemplates/treelist/state.md#expand-items-from-code) +```` +````RAZOR Columns +@[template](/_contentTemplates/treelist/state.md#column-state-from-code) +```` - public static async Task Update(Employee itemToUpdate) - { - UpdateItemRecursive(_data, itemToUpdate); - } +@[template](/_contentTemplates/grid/state.md#filter-menu-default-filters) - public static async Task Delete(Employee itemToDelete) - { - RemoveChildRecursive(_data, itemToDelete); - } - // sample helper methods for handling the view-model data hierarchy - static void UpdateItemRecursive(List items, Employee itemToUpdate) - { - for (int i = 0; i < items.Count; i++) - { - if (items[i].Id.Equals(itemToUpdate.Id)) - { - items[i] = itemToUpdate; - return; - } - - if (items[i].DirectReports?.Count > 0) - { - UpdateItemRecursive(items[i].DirectReports, itemToUpdate); - } - } - } +## Equals Comparison - static void RemoveChildRecursive(List items, Employee item) - { - for (int i = 0; i < items.Count(); i++) - { - if (item.Equals(items[i])) - { - items.Remove(item); - - return; - } - else if (items[i].DirectReports?.Count > 0) - { - RemoveChildRecursive(items[i].DirectReports, item); - - if (items[i].DirectReports.Count == 0) - { - items[i].HasChildren = false; - } - } - } - } +State properties that pertain to data items (for example, edited item or selected items) are typed according to the TreeList model. If you restore such data, make sure to implement appropriate comparison checks - by default the [`.Equals()`](https://learn.microsoft.com/en-us/dotnet/api/system.object.equals) check for a class (object) is a reference check and the reference from the restored state is very unlikely to match the current reference in the TreeList data. Thus, you may want to [override the `.Equals()` method of the TreeList model class](slug:grid-kb-save-load-state-localstorage), so that it compares by ID, or otherwise re-populate the models in the state object with the new model references from the TreeList data. - static void InsertItemRecursive(List Data, Employee insertedItem, Employee parentItem) - { - insertedItem.Id = LastId++; - if (parentItem != null) - { - parentItem.HasChildren = true; - if (parentItem.DirectReports == null) - { - parentItem.DirectReports = new List(); - } - - parentItem.DirectReports.Insert(0, insertedItem); - } - else - { - Data.Insert(0, insertedItem); - } - } - } -} -```` -### Get Current Columns Visibility, Order, Field +## Examples -The `ColumnStates` field of the state object provides you with information about the current columns in the TreeList. The `Index` field describes the position the user chose, and the `Visible` parameter indicates whether the column is hidden or not. By looping over that collection you can know what the user sees. You could, for example, sort by the index and filter by the visibility of the columns to approximate the view of the user. +You can find multiple examples for using the TreeList state in the following [Knowledge Base articles](/knowledge-base): -````RAZOR -@[template](/_contentTemplates/treelist/state.md#get-column-state-from-code) -```` +* [Save and load the TreeList state from `localStorage`](slug:grid-kb-save-load-state-localstorage) +* [Save the TreeList state in a WebAssembly app](slug:grid-kb-save-state-in-webassembly) +* [Override a user action that changes the TreeList state, for example, sort descending first](slug:grid-kb-sort-descending) +* [Initiate programmatic editing or inserting of a TreeList row](slug:treelist-kb-add-edit-state) +* [Get current TreeList column state (order index, width, and others)](slug:grid-kb-column-state) ## See Also - * [Live Demo: TreeList State](https://demos.telerik.com/blazor-ui/treelist/persist-state) - +* [Live Demo: TreeList State](https://demos.telerik.com/blazor-ui/treelist/persist-state) +* [TreeListState API reference](slug:Telerik.Blazor.Components.TreeListState-1) +* [Blazor TreeList](slug:treelist-overview) diff --git a/docs-builder.yml b/docs-builder.yml index c74ab32927..54c3629699 100644 --- a/docs-builder.yml +++ b/docs-builder.yml @@ -406,9 +406,6 @@ meta: "*components/grid/filter": title: Filtering position: 22 - "*components/grid/editing/built-in-dialogs": - title: Built-in Dialogs - position: 17 "*components/grid/editing": title: Editing position: 10 diff --git a/knowledge-base/common-stackoverflowexception-editing-circular-references.md b/knowledge-base/common-stackoverflowexception-editing-circular-references.md index 5cea5d9778..48a20d558b 100644 --- a/knowledge-base/common-stackoverflowexception-editing-circular-references.md +++ b/knowledge-base/common-stackoverflowexception-editing-circular-references.md @@ -56,7 +56,7 @@ public class Employee ## See Also -* [Grid Editing](slug:components/grid/editing/overview) +* [Grid Editing](slug:grid-editing-overview) * [Gantt Editing](slug:gantt-tree-editing) * [ListView Editing](slug:listview-editing) * [Scheduler Editing](slug:scheduler-appointments-edit) diff --git a/knowledge-base/grid-add-edit-state.md b/knowledge-base/grid-add-edit-state.md index 32e5fe3ecf..7447c1e68a 100644 --- a/knowledge-base/grid-add-edit-state.md +++ b/knowledge-base/grid-add-edit-state.md @@ -5,7 +5,7 @@ type: how-to page_title: Enter and Exit Grid Edit Mode from Code slug: grid-kb-add-edit-state position: -tags: grid, state +tags: grid, state, editing ticketid: res_type: kb --- @@ -24,17 +24,12 @@ res_type: kb ## Description -How to enter edit mode from code? - -How to initiate insert and edit operations programmatically, instead of using [command buttons](slug:components/grid/columns/command)? - -How to add new Grid rows with a custom button, which is outside the component? - -How to insert Grid rows with an external button? - -How to cancel Grid edit mode programmatically? - -How to implement Grid command buttons outside the Grid? +* How to enter edit mode from code? +* How to initiate insert and edit operations programmatically, instead of using [command buttons](slug:components/grid/columns/command)? +* How to add new Grid rows with a custom button, which is outside the component? +* How to insert Grid rows with an external button? +* How to cancel Grid edit mode programmatically? +* How to implement Grid command buttons outside the Grid? ## Solution @@ -46,7 +41,7 @@ This scenario requires knowledge about the [Grid State](slug:grid-state). Get fa To enter and exit edit mode, set the following properties of the `GridState` object: -* `InsertedItem` must be a new data item instance that will potentially be added to the Grid. You can set some default values, if needed. +* `InsertedItem` must be a new data item instance that will potentially be added to the Grid. Applicable only for `Inline` and `Popup` edit mode. You can set some default values, if needed. * `OriginalEditItem` must be a reference to an existing data item. * `EditItem` must be a clone (copy) of the `OriginalEditItem`. Later it will either update the original item, or be discarded. * `EditField` is used for incell editing only. It determines which cell will render an editor. @@ -56,9 +51,9 @@ Each property that is not relevant to a desired Grid state, should be set to `nu ## Example -The sample below shows how to add, edit, cancel and save items in [`Inline`](slug:components/grid/editing/inline) and [`Popup`](slug:components/grid/editing/popup) `EditMode`. +The sample below shows how to add, edit, cancel and save items in [`Inline`](slug:grid-editing-inline) and [`Popup`](slug:grid-editing-popup) `EditMode`. -All these operations can also be used for [`Incell`](slug:components/grid/editing/incell). However, blurring the edited cell triggers [`OnUpdate`](slug:components/grid/editing/overview#events), so external UI to manage the Grid doesn't make sense. Some special [`EditorTemplate`](slug:grid-templates-editor) may benefit from programmatic incell cancel or update. The required logic is the same as with inline editing, with the addition of `EditField`. +All these operations can also be used for [`Incell`](slug:grid-editing-incell). However, blurring the edited cell triggers [`OnUpdate`](slug:grid-editing-overview#events), so external UI to manage the Grid doesn't make sense. Some special [`EditorTemplate`](slug:grid-templates-editor) may benefit from programmatic incell cancel or update. The required logic is the same as with inline editing, with the addition of `EditField`. >caption Enter and exit Grid edit mode programmatically diff --git a/knowledge-base/grid-binding-to-expando-object.md b/knowledge-base/grid-binding-to-expando-object.md index 2bcf84068b..60cc59733b 100644 --- a/knowledge-base/grid-binding-to-expando-object.md +++ b/knowledge-base/grid-binding-to-expando-object.md @@ -39,7 +39,7 @@ The key points in the required implementation are: * Set the [`FieldType`](slug:components/grid/columns/bound#data-binding) parameter of all [bound columns](slug:components/grid/columns/bound). * If you need [autogenerated Grid columns](slug:grid-columns-automatically-generated), then [define them in a loop inside the `` tag](slug:grid-kb-dynamic-columns-with-static), which is a standard Blazor `RenderFragment`. * If Grid editing is enabled and the columns are created from the keys of the first data item, use the [`OnModelInit` event](slug:grid-events#onmodelinit) to populate each newly added item with some default values. Otherwise the columns will disappear because the new data item (`ExpandoObject`) has no properties and is prepended to the `Data` collection. Another option is to create columns from a non-first item. `null` does not qualify as a default value, because it cannot help determine the property type. -* The Grid clones data items during editing. When editing, you can store the original item in the [Grid `OnEdit` handler](slug:components/grid/editing/overview#events). This will make the retrieval of the original item easier later in `OnUpdate` (example below). +* The Grid clones data items during editing. When editing, you can store the original item in the [Grid `OnEdit` handler](slug:grid-editing-overview#events). This will make the retrieval of the original item easier later in `OnUpdate` (example below). * Nullable data, `LoadGroupsOnDemand`, and nested model properties require special handling. [Check the notes](#notes) below for additional information. >caption Grid data operations with ExpandoObject @@ -189,7 +189,7 @@ The following list contains information about requirements and limitations when ### Editing -If any non-string property in the `ExpandoObject` is `nullable`, then do not set [`EditorType`](slug:components/grid/editing/overview#customize-the-editor-fields) for the column. Instead, use a [Grid column `EditorTemplate`](slug:grid-templates-editor). The following code snippet is part of the full example below. +If any non-string property in the `ExpandoObject` is `nullable`, then do not set [`EditorType`](slug:grid-editing-overview#column-editors) for the column. Instead, use a [Grid column `EditorTemplate`](slug:grid-templates-editor). The following code snippet is part of the full example below.
diff --git a/knowledge-base/grid-checkbox-editing.md b/knowledge-base/grid-checkbox-editing.md index 5bc1259559..493f8b2ff9 100644 --- a/knowledge-base/grid-checkbox-editing.md +++ b/knowledge-base/grid-checkbox-editing.md @@ -38,7 +38,7 @@ This KB article answers the following questions: ## Solution 1. Define a [`` with a `Field`](slug:components/grid/columns/bound) that points to a `bool` property. -1. Set [`Editable="false"`](slug:components/grid/columns/bound#data-operations) for that column, if the [Grid `EditMode`](slug:components/grid/editing/overview) is `Incell`. +1. Set [`Editable="false"`](slug:components/grid/columns/bound#data-operations) for that column, if the [Grid `EditMode`](slug:grid-editing-overview) is `Incell`. 1. Define a [column cell template](slug:grid-templates-column) (`