diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000000..eb5cfec430 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,552 @@ +# Blazor Code Style Guide + +This guide outlines best practices for Razor component development and documentation in Blazor. + +## Razor File Structure + +Use the following top-down order in `.razor` files: + +1. `@page` +2. `@using` +3. `@namespace` +4. `@inherits` +5. `@implements` +6. `@inject` + +Insert ` -```` + ## See Also diff --git a/components/filemanager/data-binding/hierarchical-data.md b/components/filemanager/data-binding/hierarchical-data.md index 2cb836510e..a80df51fa9 100644 --- a/components/filemanager/data-binding/hierarchical-data.md +++ b/components/filemanager/data-binding/hierarchical-data.md @@ -14,7 +14,7 @@ This article explains how to bind the FileManager for Blazor to hierarchical dat Hierarchical data means that the collection of child items is provided in a field of its parent's model: -* The `Items` field contains a collection of all children including sub-folders and files. They will be rendered in the [Preview Pane](slug:filemanager-preview-pane) when the parent folder is selected. +* The `Items` field contains a collection of all children including sub-folders and files. They will be rendered in the [FileManager View](slug:filemanager-views) when the parent folder is selected. * The `Directories` field contains a collection of the subfolders of a directory. They will be rendered in the TreeView navigation pane. If there are `Directories` for a specific folder it will have an expand icon. The `HasDirectories` field can override this, however, but it is not required for hierarchical data binding. diff --git a/components/filemanager/data-binding/overview.md b/components/filemanager/data-binding/overview.md index f521daf2cf..6c765f6844 100644 --- a/components/filemanager/data-binding/overview.md +++ b/components/filemanager/data-binding/overview.md @@ -8,26 +8,20 @@ published: True position: 0 --- -# FileManager Data Binding Basics +# FileManager Data Binding Overview -This article explains the different ways to provide data to a FileManager component and the properties related to data binding. Reviewing this article will explain the basics of how you can describe the hierarchy of items in your data source to the treeview component so they can render. +This FileManager Data Binding section explains the different ways to provide data to a FileManager component and the properties related to data binding. This article describes what are the available (bindable) features of FileManager data items and how to map model properties to these features. -@[template](/_contentTemplates/common/general-info.md#valuebind-vs-databind-link) - -First, review: - -* The available (bindable) [features of a FileManager items](#filemanager-item-features). -* How to match fields in the model with the FileManager item [data bindings](#data-bindings). +There are two data binding modes that the FileManager supports: -There are two modes of providing data to a FileManager, and they both use the items' features. Once you are familiar with the current article, choose the data binding more you wish to use: +* [Flat data](slug:filemanager-data-binding-flat-data)—a collection of self-referencing items with parent-child relationships. +* [Hierarchical data](slug:filemanager-data-binding-hierarchical-data)—each item holds its children in a nested property. -* [Flat data](slug:filemanager-data-binding-flat-data) - a collection of self-referencing items with parent-child relationships See the `Id` and `ParentId` settings. - -* [Hierarchical data](slug:filemanager-data-binding-hierarchical-data) - each item holds its children in a nested property. See the `Directories` setting. +@[template](/_contentTemplates/common/general-info.md#valuebind-vs-databind-link) ## FileManager Item Features -The FileManager has features that map to properties in the model. The following model uses property names that will work automatically, with no additional FileManager configuration: +The FileManager extracts information about the displayed files and folders from properties in the model. The following flat data model uses property names that will work automatically, with no additional FileManager configuration: ````RAZOR @using System.IO @@ -225,62 +219,41 @@ The FileManager has features that map to properties in the model. The following } ```` -The above model properties have the following meaning for the FileManager: - +The following section describes the meaning of the model properties for the FileManager. -@[template](/_contentTemplates/common/parameters-table-styles.md#table-layout) +## Data Bindings -| Property | Description | -| --- | --- | -| **Item features** | | -| `Name` | The name of the file. | -| `Size` | The size of the file. | -| `Path` | The path of the file. | -| `Extension` | The extension of the file. | -| `IsDirectory` | Whether the item is a directory. If its value is `false` the item is considered a file and not a directory. | -| `DateCreated` | The creation date of the file. | -| `DateCreateUtc` | The creation date of the file in UTC. Required. | -| `DateModified` | The modification date of the file. | -| `DateModifiedUtc` | The modification date of the file in UTC. Required. | -| **Item relations** | | -| `Id `| The unique identifier of the file. Required for [**binding to flat data**](slug:filemanager-data-binding-flat-data). | -| `ParentId` | Identifies the file's parent. Required for [**binding to flat data**](slug:filemanager-data-binding-flat-data). Set to `null` for root items. Do *not* use `ParentId` with hierarchical data. | -| `HasDirectories` | Determines whether the item has subdirectories. Required for binding to [**flat data**](slug:filemanager-data-binding-flat-data) If `true`, the directory will show an expand arrow. With [**hierarchical data**](slug:filemanager-data-binding-hierarchical-data), the FileManager renders expand icons based on `Directories`, but `HasDirectories` will take precedence. | -| `Directories` | Defines the item subdirectories. Required for [binding to **hierarchical data**](slug:filemanager-data-binding-hierarchical-data). -| `Items` | Defines all the subitems (directories and files) of the item. | +All [FileManager item features](#fileManager-item-features) map to model properties. You define that relationship by providing the property name from which the corresponding information is taken. To do this, use the following parameters of the main `TelerikFileManager` tag: -## Data Bindings +@[template](/_contentTemplates/common/parameters-table-styles.md#table-layout) -All [FileManager item features](#fileManager-item-features) map to model properties. The properties of a treeview item match directly to a field of the model the treeview is bound to. You provide that relationship by providing the name of the field from which the corresponding information is to be taken. To do this, in the main `TelerikFileManager` tag, use the parameters described below: - -| FileManager Parameter | Default Value | -| --- | --- | -| **Item features** | | -| `NameField`| `"Name"` | -| `SizeField`| `"Size"` | -| `PathField`| `"Path"` | -| `ExtensionField`| `"Extension"` | -| `IsDirectoryField`| `"IsDirectoryField"` | -| `DateCreatedField`| `"DateCreated"` | -| `DateCreateUtcField`| `"DateCreateUtc"` | -| `DateModifiedField`| `"DateModified"` | -| `DateModifiedUtcField`| `"DateModifiedUtc"` | -| **Item relations** | | -| `IdField `| `"Id" ` | -| `ParentIdField`| `"ParentId"` | -| `HasDirectoriesField`| `"HasDirectories"` | -| `DirectoriesField`| `"Directories"` | -| `EntriesField`| `"Entries"` | - ->important Do not use `ParentId` with hierarchical data. This will confuse the FileManager that it is bound to flat data and the component may not render any items. If the model must have a `ParentId` property, set `ParentIdField` to a non-existent property. +| FileManager Parameter | Default Parameter Value
(Model Property Name) | Model Property Type | Model Value Description | +| --- | --- | --- | --- | +| **Item features** | | | | +| `NameField`| `"Name"` | `string` | The name of the file or folder, excluding the extension. | +| `SizeField`| `"Size"` | `long` | The size of the file in bytes. | +| `PathField`| `"Path"` | `string` | The path to the item, including the name and extension. | +| `ExtensionField`| `"Extension"` | `string` | The item extension, starting with a dot `.` | +| `IsDirectoryField`| `"IsDirectory"` | `bool` | Whether the item is a folder. If `false`, it's treated as a file. | +| `DateCreatedField`| `"DateCreated"` | `DateTime` | The creation date of the file. | +| `DateCreatedUtcField`| `"DateCreatedUtc"` | `DateTime` | The creation date of the file in UTC. Required. | +| `DateModifiedField`| `"DateModified"` | `DateTime` | The modification date of the file. | +| `DateModifiedUtcField`| `"DateModifiedUtc"` | `DateTime` | The modification date of the file in UTC. Required. | +| **Item relations** | | | | +| `IdField `| `"Id" ` | any | The unique identifier of the file. Required for [binding to flat data](slug:filemanager-data-binding-flat-data). | +| `ParentIdField`| `"ParentId"` | any | Identifies the item's parent. Required for [binding to flat data](slug:filemanager-data-binding-flat-data). Set to `null` for root items. Do not use `ParentId` with hierarchical data. | +| `HasDirectoriesField`| `"HasDirectories"` | `bool` | Determines whether the item has child folders. Required for [binding to flat data](slug:filemanager-data-binding-flat-data). If `true`, the folder will show an expand arrow in the TreeView. With [hierarchical data](slug:filemanager-data-binding-hierarchical-data), the FileManager renders expand icons based on `Directories`, but `HasDirectories` takes precedence. | +| `DirectoriesField`| `"Directories"` | `IEnumerable` | The item's child folders to display in the TreeView. Required for [binding to hierarchical data](slug:filemanager-data-binding-hierarchical-data). | +| `ItemsField`| `"Items"` | `IEnumerable` | The folder's child files and folders to display in the FileManager view. Required for [binding to hierarchical data](slug:filemanager-data-binding-hierarchical-data). | + +>important Do not use `ParentId` with hierarchical data. This will confuse the FileManager that it is bound to flat data and the component may not render any items. If the model must have a `ParentId` property, set `ParentIdField` to a non-existent property name. ## Next Steps -Learn the different ways to provide data to a TreeView: - -* [Use flat data](slug:filemanager-data-binding-flat-data) -* [Use hierarchical data](slug:filemanager-data-binding-hierarchical-data) - each item holds its children in a nested property +Learn the different ways to provide data to a FileManager: +* [Use flat data](slug:filemanager-data-binding-flat-data), where all items at all levels represent a single collection. +* [Use hierarchical data](slug:filemanager-data-binding-hierarchical-data), where each folder item holds its child files and folders in nested properties. ## See Also diff --git a/components/filemanager/events.md b/components/filemanager/events.md index efa8976c12..487999bce9 100644 --- a/components/filemanager/events.md +++ b/components/filemanager/events.md @@ -12,20 +12,19 @@ position: 20 This article explains the events available in the Telerik FileManager for Blazor. They are grouped logically. -* [CUD Events](#cud-events) - events related to Creating, Updating and Deleting items. +* [CRUD Events](#crud-events) - events related to Creating, Updating and Deleting items. * [OnCreate](#oncreate) - * [OnUpdate](#onupdate) - * [OnEdit](#onedit) * [OnDelete](#ondelete) -* [Read Event](#read-event) - event related to obtaining data. -* [Other Events](#other-events) - other events the grid provides. - * [OnModelInit](#onmodelinit) - * [OnDownload](#ondownload) - * [PathChanged](#pathchanged) - * [SelectedItemsChanged](#selecteditemschanged) - * [ViewChanged](#viewchanged) + * [OnEdit](#onedit) + * [OnUpdate](#onupdate) +* [OnDownload](#ondownload) +* [OnModelInit](#onmodelinit) +* [OnRead Event](#onread) - event related to obtaining data. +* [PathChanged](#pathchanged) +* [SelectedItemsChanged](#selecteditemschanged) +* [ViewChanged](#viewchanged) -## CUD Events +## CRUD Events The `OnCreate`, `OnUpdate` and `OnDelete` events let you get the data item that the user changed so you can transfer the user action to the actual data source. @@ -35,302 +34,27 @@ The `OnEdit` event let you respond to user actions - when they want to edit an i The `OnCreate` event fires when a new item is created (new folder). Its event handler receives the updated `FileManagerCreateEventArgs` as an argument. -### OnUpdate - -The `OnUpdate` event fires when a file is updated (rename finishes). Its event handler receives the updated `FileManagerUpdateEventArgs` as an argument. - -### OnEdit - -The `OnEdit` event fires when the user is about to enter edit mode for an existing item. Its event handler receives the updated `FileManagerEditEventArgs` as an argument. - ### OnDelete The `OnDelete` event fires when a file is deleted. Its event handler receives the updated `FileManagerDeleteEventArgs` as an argument. -## Read Event - -In the common case, all the data is provided to the filemanager's `Data` collection and the `FileManager` performs the operations on it for you. In some cases, you may want to do this programmatically (for example, to retrieve only a small number of items to improve the backend performance). Attach the `OnRead` event to perform all the data read operations programmatically in the `FileManager`. - -The `OnRead` event fires when the data source is read. Its event handler receives the updated `FileManagerReadEventArgs` as an argument. - ->caption Handle OnRead. - -````RAZOR -@using System.IO - - - - -@code { - private List SourceData = new List(); - - private string RootPath { get; set; } = "root-folder-path"; - private string DirectoryPath { get; set; } = "root-folder-path"; - - private async Task OnRead(FileManagerReadEventArgs args) - { - await Task.Delay(500); - - //here you can pass only the files for the current directory, so you don't load the whole data collection - args.Data = SourceData; - args.Total = SourceData.Count; - } - - private async Task OnUpdateHandler(FileManagerUpdateEventArgs args) - { - var item = args.Item as FlatFileEntry; - - if (item.IsDirectory) - { - // prevent renaming of directories. If you allow that, make sure - //to also update the Path of the children - } - else - { - // simulate update of the file name and path - var name = item.Name ?? string.Empty; - var extension = item.Extension ?? string.Empty; - var fullName = extension.Length > 0 && name.EndsWith(extension) ? - name : $"{name}{extension}"; - - var updatedItem = SourceData.FirstOrDefault(x => x.MyModelId == item.MyModelId); - - updatedItem.Name = item.Name; - updatedItem.Path = Path.Combine(DirectoryPath, fullName); - } - } - - private async Task OnDownloadHandler(FileManagerDownloadEventArgs args) - { - var selectedItem = args.Item as FlatFileEntry; - - //the FileManager does not have the actual file. - //To download it, find the actual file in the datasource - //based on the selected file (args.Item) and - //assign the following data to the argument: - - //args.Stream = the file stream of the actual selected file; - //args.MimeType = the mime type of the actual file, so it can be downloaded; - //args.FileName = allows overriding the name of the downloaded file; - } - - - private async Task OnDeleteHandler(FileManagerDeleteEventArgs args) - { - var currItem = args.Item as FlatFileEntry; - - var itemToDelete = SourceData.FirstOrDefault(x => x.MyModelId == currItem.MyModelId); - - //simulate item deletion - SourceData.Remove(itemToDelete); - - RefreshData(); - } - - private FlatFileEntry OnModelInitHandler() - { - var item = new FlatFileEntry(); - item.Name = $"New folder"; - item.Size = 0; - item.Path = Path.Combine(DirectoryPath, item.Name); - item.IsDirectory = true; - item.HasDirectories = false; - item.DateCreated = DateTime.Now; - item.DateCreatedUtc = DateTime.Now; - item.DateModified = DateTime.Now; - item.DateModifiedUtc = DateTime.Now; - - return item; - } - - private void RefreshData() - { - SourceData = new List(SourceData); - } - - // fetch the FileManager data - protected override async Task OnInitializedAsync() - { - SourceData = await GetFlatFileEntries(); - } - - // a model to bind the FileManager. Should usually be in its own separate location. - public class FlatFileEntry - { - public string MyModelId { get; set; } - public string ParentId { get; set; } - public string Name { get; set; } - public long Size { get; set; } - public string Path { get; set; } - public string Extension { get; set; } - public bool IsDirectory { get; set; } - public bool HasDirectories { get; set; } - public DateTime DateCreated { get; set; } - public DateTime DateCreatedUtc { get; set; } - public DateTime DateModified { get; set; } - public DateTime DateModifiedUtc { get; set; } - } - - // the next lines are hardcoded data generation so you can explore the FileManager freely - - private async Task> GetFlatFileEntries() - { - var workFiles = new FlatFileEntry() - { - MyModelId = "1", - ParentId = null, - Name = "Work Files", - IsDirectory = true, - HasDirectories = true, - DateCreated = new DateTime(2022, 1, 2), - DateCreatedUtc = new DateTime(2022, 1, 2), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(RootPath, "Work Files"), - Size = 3 * 1024 * 1024 - }; - - var Documents = new FlatFileEntry() - { - MyModelId = "2", - ParentId = workFiles.MyModelId, - Name = "Documents", - IsDirectory = true, - HasDirectories = false, - DateCreated = new DateTime(2022, 1, 2), - DateCreatedUtc = new DateTime(2022, 1, 2), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(workFiles.Path, "Documents"), - Size = 1024 * 1024 - }; - - var Images = new FlatFileEntry() - { - MyModelId = "3", - ParentId = workFiles.MyModelId, - Name = "Images", - IsDirectory = true, - HasDirectories = false, - DateCreated = new DateTime(2022, 1, 2), - DateCreatedUtc = new DateTime(2022, 1, 2), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(workFiles.Path, "Images"), - Size = 2 * 1024 * 1024 - }; - - var specification = new FlatFileEntry() - { - MyModelId = "4", - ParentId = Documents.MyModelId, - Name = "Specification", - IsDirectory = false, - HasDirectories = false, - Extension = ".docx", - DateCreated = new DateTime(2022, 1, 5), - DateCreatedUtc = new DateTime(2022, 1, 5), - DateModified = new DateTime(2022, 2, 3), - DateModifiedUtc = new DateTime(2022, 2, 3), - Path = Path.Combine(Documents.Path, "Specification.docx"), - Size = 462 * 1024 - }; - - var report = new FlatFileEntry() - { - MyModelId = "5", - ParentId = Documents.MyModelId, - Name = "Monthly report", - IsDirectory = false, - HasDirectories = false, - Extension = ".xlsx", - DateCreated = new DateTime(2022, 1, 20), - DateCreatedUtc = new DateTime(2022, 1, 20), - DateModified = new DateTime(2022, 1, 25), - DateModifiedUtc = new DateTime(2022, 1, 25), - Path = Path.Combine(Documents.Path, "Monthly report.xlsx"), - Size = 538 * 1024 - }; - - var dashboardDesign = new FlatFileEntry() - { - MyModelId = "6", - ParentId = Images.MyModelId, - Name = "Dashboard Design", - IsDirectory = false, - HasDirectories = false, - Extension = ".png", - DateCreated = new DateTime(2022, 1, 10), - DateCreatedUtc = new DateTime(2022, 1, 10), - DateModified = new DateTime(2022, 2, 13), - DateModifiedUtc = new DateTime(2022, 2, 13), - Path = Path.Combine(Images.Path, "Dashboard Design.png"), - Size = 1024 - }; - - var gridDesign = new FlatFileEntry() - { - MyModelId = "7", - ParentId = Images.MyModelId, - Name = "Grid Design", - IsDirectory = false, - HasDirectories = false, - Extension = ".jpg", - DateCreated = new DateTime(2022, 1, 12), - DateCreatedUtc = new DateTime(2022, 1, 12), - DateModified = new DateTime(2022, 2, 13), - DateModifiedUtc = new DateTime(2022, 2, 13), - Path = Path.Combine(Images.Path, "Grid Design.jpg"), - Size = 1024 - }; - - var files = new List() - { - workFiles, - - Documents, - specification, - report, +### OnEdit - Images, - dashboardDesign, - gridDesign - }; +The `OnEdit` event fires when the user is about to enter edit mode for an existing item. Its event handler receives the updated `FileManagerEditEventArgs` as an argument. - return await Task.FromResult(files); - } -} -```` +### OnUpdate -## Other Events +The `OnUpdate` event fires when a file is updated (rename finishes). Its event handler receives the updated `FileManagerUpdateEventArgs` as an argument. -### OnModelInit +## OnDownload -The `OnModelInit` event fires when a new instance of the model is about to be created. Handle this event to allow the creation of a new folder/file. Provide an instance of the model that the component is bound to and include the desired properties (name, path, date of creation and more). See the [example](#example). +The `OnDownload` event fires before a file download starts. The event is cancellable. The event handler argument is an `FileManagerDownloadEventArgs` object. See the [example](#example). -### OnDownload +### MIME Type -The `OnDownload` event fires before a file download starts. The event is cancellable. The event handler argument is an `FileManagerDownloadEventArgs` object. See the [example](#example). +The `FileManagerDownloadEventArgs` event argument has a `MimeType` property, which is `null` when the `OnDownload` event fires. .NET does not provide a built-in MIME type tool, so the application must set the correct MIME type, depending on the file extension or content. Consider [`Microsoft.AspNetCore.StaticFiles`](https://github.com/dotnet/aspnetcore/blob/main/src/Middleware/StaticFiles/src/FileExtensionContentTypeProvider.cs) or a similar tool. -#### Downloading Large Files +### Downloading Large Files The files are downloaded with the help of a Base64 data URL, which is sent to the browser through `JSInterop`. JavaScript code generates an `` tag with an [object URL](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL_static) on the web page and the tag is clicked programmatically, so that the browser shows its **Save File** dialog. @@ -341,7 +65,7 @@ Large files (tens or hundreds of megabytes) may hit the browser's max data URL l >tip You can also vote for the [FileManager feature request to expose a Proxy Url](https://feedback.telerik.com/blazor/1633629) for serving files from the server to the browser. -#### Downloading Server Files in WebAssembly Apps +### Downloading Server Files in WebAssembly Apps A FileManager in a WebAssembly app usually displays files from a remote server. In such cases, use the following download approach: @@ -349,15 +73,31 @@ A FileManager in a WebAssembly app usually displays files from a remote server. 1. The server returns the file content. 1. The `OnDownload` handler puts the returned file content to a `MemoryStream` and assigns it to `args.Stream`. -### PathChanged +## OnModelInit + +The `OnModelInit` event fires when a new instance of the model is about to be created. Handle this event to allow the creation of a new folder/file. Provide an instance of the model that the component is bound to and include the desired properties (name, path, date of creation and more). See the [example](#example). + +## OnRead + +The `OnRead` event is an alternative way to provide the FileManager with data, instead of the `Data` parameter. The event fires when the FileManager is initialized. The event handler receives a `FileManagerReadEventArgs` object as an argument. + +Use the `OnRead` event if you want to load chunks of FileManager data on demand. The following API members are required: + +* `PathChanged` event +* `Rebind()` method +* `OnRead` event + +For more information, refer to [How to Load FileManager File Data on Demand](slug:filemanager-kb-load-file-data-on-demand). + +## PathChanged The `PathChanged` event fires when the user navigates to a different folder through the TreeView or by double-clicking a folder item in the [FileManager View](slug:filemanager-views). The event handler receives the new path as a `string` argument. -### SelectedItemsChanged +## SelectedItemsChanged The `SelectedItemChanged` event fires every time the user clicks on a new file/folder in the main pane of the FileManager. You can use it with one-way binding of the `SelectedItems` parameter to respond to user selection. -### ViewChanged +## ViewChanged The `ViewChanged` event fires when the user toggles between the [two FileManager views (`Grid` and `ListView`)](slug:filemanager-views). If you are using the event, make sure to update the value of the `View` parameter, otherwise the user action will have no effect. @@ -477,24 +217,31 @@ The `ViewChanged` event fires when the user toggles between the [two FileManager private async Task OnDownloadHandler(FileManagerDownloadEventArgs args) { - var selectedItem = args.Item as FlatFileEntry; + var downloadedFile = (FlatFileEntry)args.Item; // The Filemanager does not have the actual file. - // Obtain the file contents, based on args.Item, and set the event arguments: + // Obtain the file contents, based on args.Item, and set the event arguments. + + // Get the full file path + //string filePathWithoutStartSeparator = downloadedFile.Path.IndexOf(Path.DirectorySeparatorChar) == 0 ? downloadedFile.Path.Substring(1) : downloadedFile.Path; + //string fullFilePath = Path.Combine(BasePath, filePathWithoutStartSeparator); - //args.Stream = the file stream of the actual selected file; - //args.MimeType = the MIME type of the actual file; - //args.FileName = the file name that the browser will receive (optional); + // Create a MemoryStream with the file contents + //byte[] fileBytes = await System.IO.File.ReadAllBytesAsync(fullFilePath); + //MemoryStream fileStream = new(fileBytes); - FlatFileEntry actualFile = (FlatFileEntry)args.Item; + // Set the event arguments + //args.FileName = string.Concat(downloadedFile.Name, downloadedFile.Extension); + //args.MimeType = "application/octet-stream"; + //args.Stream = fileStream; await Task.Delay(1); // simulate async operation - string dummyFileContent = $"This file is a dummy version of {actualFile.Name}. It was downloaded with the Telerik Blazor FileManager."; + string dummyFileContent = $"This file is a dummy version of {downloadedFile.Name}. It was downloaded with the Telerik Blazor FileManager."; byte[] dummyFileBuffer = System.Text.Encoding.UTF8.GetBytes(dummyFileContent); args.Stream = new MemoryStream(dummyFileBuffer); args.MimeType = "text/plain"; - args.FileName = $"filemanager-{actualFile.Name}-{DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")}.txt"; + args.FileName = $"filemanager-{downloadedFile.Name}-{DateTime.Now.ToString("HH-mm-ss")}.txt"; } private async Task OnDeleteHandler(FileManagerDeleteEventArgs args) diff --git a/components/fileselect/events.md b/components/fileselect/events.md index 9ca1be2164..b0659c79e4 100644 --- a/components/fileselect/events.md +++ b/components/fileselect/events.md @@ -27,7 +27,7 @@ The `FileSelectFileInfo` type contains the following properties: Property | Type | Description ---------|----------|--------- `Id` | `string` | The unique file identifier. -`Name`|`string` | The file name. +`Name`|`string` | The encoded file name, including the extension. One method to decode it is [`System.Net.WebUtility.HtmlDecode()`](https://learn.microsoft.com/en-us/dotnet/api/system.net.webutility.htmldecode). The file can be renamed in the [`OnSelect` event handler](#onselect). `Size` |`long` | The file size in bytes. `Extension` |`string` | The file extension. `InvalidExtension` | `bool` | A Boolean flag that shows if the file type is invalid. diff --git a/components/filter/events.md b/components/filter/events.md index a8dc8fe6ff..a11a675e6e 100644 --- a/components/filter/events.md +++ b/components/filter/events.md @@ -12,13 +12,67 @@ position: 11 This article explains the available events for the Telerik Filter for Blazor: +* [OnUpdate](#onupdate) * [ValueChanged](#valuechanged) +## OnUpdate + +The `OnUpdate` event fires when the user changes the Filter `Value`. The component is designed for one-way binding and works directly with the object reference of the bound `CompositeFilterDescriptor`. The component updates the `Value` internally. Use the `OnUpdate` event to handle any additional logic when the Filter `Value` is modified. + +>caption Handle OnUpdate + +````RAZOR +@using Telerik.DataSource + +
Change any filter value to trigger the event and see the message update from the OnUpdate handler.
+ + + + + + + + +
+
+ @EventMessage +
+ + + +@code { + private CompositeFilterDescriptor Value { get; set; } = new CompositeFilterDescriptor(); + private string EventMessage { get; set; } = string.Empty; + + private void OnFilterUpdate() + { + EventMessage = $"Filter updated at {DateTime.Now:HH:mm:ss}"; + } + + public class Person + { + public int EmployeeId { get; set; } + public string Name { get; set; } = string.Empty; + public int AgeInYears { get; set; } + } +} +```` + ## ValueChanged The `ValueChanged` event fires when the value has changed. Its event handler receives the updated `CompositeFilterDescriptor` as an argument. ->caption Handle ValueChanged. +> The `ValueChanged` event is deprecated and will be removed in future versions. Use the `OnUpdate` event instead. + +>caption Handle ValueChanged ````RAZOR @* This code snippet showcases an example of how to handle the ValueChanged event. *@ diff --git a/components/form/formgroups.md b/components/form/formgroups.md index 5ff7da282c..5919bfc08b 100644 --- a/components/form/formgroups.md +++ b/components/form/formgroups.md @@ -25,7 +25,7 @@ The `FormGroup` tag exposes the following parameters: * `Columns` - `int` - defines the number of columns in the group. -* `ColumnSpacing` - `string` - defines the space between the editors in the group. +* `ColumnSpacing` - `string` - defines the horizontal space between the editors in the group. ## Example - Organize FormItems into Groups @@ -51,7 +51,7 @@ You can organize some FormItems into logical groups. You can configure the label - + diff --git a/components/form/overview.md b/components/form/overview.md index 57643d1715..989fe67a82 100644 --- a/components/form/overview.md +++ b/components/form/overview.md @@ -207,7 +207,7 @@ The [Blazor Form](https://demos.telerik.com/blazor-ui/form/overview) exposes mul |-----------|------------------------|-------------| | `ButtonsLayout` | `FormButtonsLayout` enum
(`Start`) | Determines the position and width of all Form buttons. See [Form Buttons](slug:form-formitems-buttons). | | `Columns` | `int` | Defines the number of columns in the Form. See the [Columns](slug:form-columns) article for more information | -| `ColumnSpacing` | `string` | Defines the amout of vertical space between the Columns. See the [Columns](slug:form-columns) article for more information. | +| `ColumnSpacing` | `string` | Defines the amout of horizontal space between the Columns. See the [Columns](slug:form-columns) article for more information. | | `Orientation` | `FormOrientation` enum
(`Vertical`) | Determines the position of each label with regard to its editor. See [Orientation](slug:form-orientation) for more information. | ### Styling and Appearance diff --git a/components/form/validation.md b/components/form/validation.md index 81d5171fb5..f092d624ff 100644 --- a/components/form/validation.md +++ b/components/form/validation.md @@ -133,7 +133,6 @@ You can use the built-in `DataAnnotationsValidator` that comes with the Blazor f - @code { public Person person { get; set; } = new Person(); @@ -158,8 +157,7 @@ You can use the
-````RAZOR +````RAZOR.skip-repl @using System.Dynamic @using System.ComponentModel.DataAnnotations @@ -228,50 +226,122 @@ When using a model with nested objects and fields, specify their `Field` setting ### Fluent Validation -You can use third-party validation libraries that integrate with the standard `EditContext` such as FluentValidation together with the Telerik Form for Blazor. +You can use third-party validation libraries that integrate with the standard `EditContext` such as [FluentValidation](https://fluentvalidation.net/) together with the Telerik Form for Blazor. ->note Such third party tools are not included with the Telerik UI for Blazor package. Your project must reference their NuGet packages explicitly. The code snippet below will not run unless you install the an appropriate package first. You can find some in their official documentation. +The example below: +* Requires the [`Blazored.FluentValidation` NuGet package](https://www.nuget.org/packages/Blazored.FluentValidation). Also refer to the [FluentValidation documentation](https://docs.fluentvalidation.net/en/latest/blazor.html). +* Shows how to pass `ValueExpression` from a parent component to optional custom child components in a [Form item template](slug:form-formitems-template) or a [Grid editor template](slug:grid-templates-editor). If the `ValueExpression` is not passed correctly, the app will throw [exception similar to: `Cannot validate instances of type 'ComponentName'. This validator can only validate instances of type 'ModelClassName'`](slug:form-kb-fluent-validation-cannot-validate-instances-of-type). >caption Using FluentValidation -
-````RAZOR -@using Microsoft.AspNetCore.Components.Forms -@using FluentValidation +````RAZOR Home.razor @using Blazored.FluentValidation +@using FluentValidation -
- - - - - -
+ + + + + + + + + + + + + + + + + +

@FormSubmitResult

@code { - public EditContext EditContext {get; set; } - public Customer MyModel { get; set; } = new Customer(); - public CustomerValidator Validator { get; set; } = new CustomerValidator(); + private Person PersonToEdit { get; set; } = new(); + + public PersonValidator PersonFluentValidator { get; set; } = new(); - protected override void OnInitialized() + private string FormSubmitResult { get; set; } = string.Empty; + + private void LastNameChanged(string newLastName) { - EditContext = new EditContext(MyModel); - base.OnInitialized(); + PersonToEdit.LastName = newLastName; } - public class Customer + private void OnFormValidSubmit() { - public string FirstName { get; set; } - public string LastName { get; set; } + FormSubmitResult = $"Form Submit Success at {DateTime.Now.ToString("HH:mm:ss")}"; + } + + public class PersonValidator : AbstractValidator + { + public PersonValidator() + { + RuleFor(customer => customer.FirstName).NotEmpty().MinimumLength(2).MaximumLength(60); + RuleFor(customer => customer.MiddleName).NotEmpty().MaximumLength(60); + RuleFor(customer => customer.LastName).NotEmpty().MinimumLength(2).MaximumLength(60); + } + } + + public class Person + { + public int Id { get; set; } + + public string FirstName { get; set; } = string.Empty; + + public string MiddleName { get; set; } = string.Empty; + + public string LastName { get; set; } = string.Empty; } +} +```` +````RAZOR TextBox.razor +@using System.Linq.Expressions + + + +@code { + [Parameter] + public string Value { get; set; } = string.Empty; + + [Parameter] + public EventCallback ValueChanged { get; set; } + + [Parameter] + public Expression>? ValueExpression { get; set; } - public class CustomerValidator : AbstractValidator + [Parameter] + public string Id { get; set; } = string.Empty; + + private async Task TextBoxValueChanged(string newValue) { - public CustomerValidator() + Value = newValue; + + if (ValueChanged.HasDelegate) { - RuleFor(customer => customer.FirstName).NotEmpty().MaximumLength(50); - RuleFor(customer => customer.LastName).NotEmpty().MaximumLength(50); + await ValueChanged.InvokeAsync(newValue); } } } diff --git a/components/gantt/gantt-tree/editing/incell.md b/components/gantt/gantt-tree/editing/incell.md index a1b3e9d3ba..96e93d5a74 100644 --- a/components/gantt/gantt-tree/editing/incell.md +++ b/components/gantt/gantt-tree/editing/incell.md @@ -18,10 +18,18 @@ Command columns and non-editable columns are skipped while tabbing. The InCell edit mode provides a specific user experience and behaves differently than other edit modes. Please review the notes below to get a better understanding of these specifics. +## New Row Position + +To control whether a newly added item appears at the top or bottom of the Gantt Tree, set the `NewRowPosition` parameter. + +The `NewRowPosition` parameter accepts values from the `GanttTreeListNewRowPosition` enum: + +- `Top` (default)—Inserts the new item at the top of the view. +- `Bottom`—Inserts the new item at the bottom of the view. + ### Note It is up to the data access logic to save the data once it is changed in the data collection, or to revert changes. The example above showcases the events that allow you to do that. In a real application, the code for handling data operations may be entirely different. - >caption InCell Editing Example. ````RAZOR @@ -68,20 +76,9 @@ It is up to the data access logic to save the data once it is changed in the dat @code { - public DateTime SelectedDate { get; set; } = new DateTime(2019, 11, 11, 6, 0, 0); - - class FlatModel - { - public int Id { get; set; } - public int? ParentId { get; set; } - public string Title { get; set; } - public double PercentComplete { get; set; } - public DateTime Start { get; set; } - public DateTime End { get; set; } - } - - public int LastId { get; set; } = 1; - List Data { get; set; } + private DateTime SelectedDate { get; set; } = new DateTime(2019, 11, 11, 6, 0, 0); + private int LastId { get; set; } = 1; + private List Data { get; set; } protected override void OnInitialized() { @@ -122,6 +119,16 @@ It is up to the data access logic to save the data once it is changed in the dat base.OnInitialized(); } + public class FlatModel + { + public int Id { get; set; } + public int? ParentId { get; set; } + public string Title { get; set; } + public double PercentComplete { get; set; } + public DateTime Start { get; set; } + public DateTime End { get; set; } + } + private async Task CreateItem(GanttCreateEventArgs args) { var argsItem = args.Item as FlatModel; @@ -251,4 +258,3 @@ It is up to the data access logic to save the data once it is changed in the dat ## See Also * [Live Demo: Gantt InCell Editing](https://demos.telerik.com/blazor-ui/gantt/editing-incell) - diff --git a/components/gantt/gantt-tree/editing/inline.md b/components/gantt/gantt-tree/editing/inline.md index 55ca17ea03..690e11689c 100644 --- a/components/gantt/gantt-tree/editing/inline.md +++ b/components/gantt/gantt-tree/editing/inline.md @@ -20,6 +20,14 @@ You can also cancel the events by setting the `IsCancelled` property of the even To enable Inline editing in the Gantt Tree, set its `TreeListEditMode` property to `GanttTreeListEditMode.Inline`, then handle the CRUD events as shown in the example below. +## New Row Position + +To control whether a newly added item appears at the top or bottom of the Gantt Tree, set the `NewRowPosition` parameter. + +The `NewRowPosition` parameter accepts values from the `GanttTreeListNewRowPosition` enum: + +- `Top` (default)—Inserts the new item at the top of the view. +- `Bottom`—Inserts the new item at the bottom of the view. >caption The Command buttons and the Gantt events let you handle data operations in Inline edit mode. @@ -70,20 +78,9 @@ To enable Inline editing in the Gantt Tree, set its `TreeListEditMode` property @code { - public DateTime SelectedDate { get; set; } = new DateTime(2019, 11, 11, 6, 0, 0); - - class FlatModel - { - public int Id { get; set; } - public int? ParentId { get; set; } - public string Title { get; set; } - public double PercentComplete { get; set; } - public DateTime Start { get; set; } - public DateTime End { get; set; } - } - - public int LastId { get; set; } = 1; - List Data { get; set; } + private DateTime SelectedDate { get; set; } = new DateTime(2019, 11, 11, 6, 0, 0); + private int LastId { get; set; } = 1; + private List Data { get; set; } protected override void OnInitialized() { @@ -124,6 +121,16 @@ To enable Inline editing in the Gantt Tree, set its `TreeListEditMode` property base.OnInitialized(); } + public class FlatModel + { + public int Id { get; set; } + public int? ParentId { get; set; } + public string Title { get; set; } + public double PercentComplete { get; set; } + public DateTime Start { get; set; } + public DateTime End { get; set; } + } + private async Task CreateItem(GanttCreateEventArgs args) { var argsItem = args.Item as FlatModel; diff --git a/components/gantt/gantt-tree/editing/overview.md b/components/gantt/gantt-tree/editing/overview.md index b25d18813b..deacf14f0c 100644 --- a/components/gantt/gantt-tree/editing/overview.md +++ b/components/gantt/gantt-tree/editing/overview.md @@ -298,6 +298,30 @@ You can customize the editors rendered in the Gantt Tree by providing the `Edito * `IsCanceled`- a boolean field indicating whether the operation is to be prevented. +## New Row Position + +You can control whether a newly added item appears at the top or bottom of the Gantt Tree. Use the [`NewRowPosition`](https://www.telerik.com/blazor-ui/documentation/api/telerik.blazor.gantttreelistnewrowposition) parameter to specify the position. This parameter does not affect Popup edit mode, which always displays a dialog for new items. + +This configuration is available in InCell and Inline edit modes. For more details, see the [Tree InCell Editing](slug:gant-tree-incell-editing#new-row-position) and [Tree Inline Editing](slug:gant-tree-inline-editing#new-row-position) articles. + +> When you set `NewRowPosition` to `Bottom`, add the new item at the end of your data collection in the `OnCreate` event handler. When set to `Top`, insert the new item at the beginning of the collection. This ensures the new row appears in the correct position in the view after successfully creating the new record. + +>caption Example of adding a new item to the Gantt based on the `NewRowPosition` value + +
+````C# +private void OnCreate(GanttCreateEventArgs args) +{ + if (NewRowPosition == GanttTreeListNewRowPosition.Bottom) + { + dataCollection.Add(newItem); + } + else // Top + { + dataCollection.Insert(0, newItem); + } +} +```` ## Example diff --git a/components/gauges/arc/labels.md b/components/gauges/arc/labels.md index 48ece6bda1..0455af3ed6 100644 --- a/components/gauges/arc/labels.md +++ b/components/gauges/arc/labels.md @@ -13,13 +13,10 @@ position: 20 You can customize the appearance of the labels rendered on the [scale](slug:arc-gauge-scale) of the Arc Gauge by using the ``, child tag of the ``, and the parameters it exposes: * [Format](#format) - * [Center Template](#center-template) - +* [Position](#position) * [Color](#color) - * [Visible](#visible) - * [Additional Customization](#additional-customization) ## Format @@ -87,6 +84,42 @@ The center template allows you to take control of the rendering of the central s ```` +## Position + +The `Position` parameter is of enum type `ArcGaugeScaleLabelsPosition` and determines whether the Gauge labels are on the inside (default) or outside of the Gauge graphic. Labels on the inside allow for a visually larger component within the same available space. + +>caption Setting Arc Gauge label position + +````RAZOR + + + + + + + + + + + + + + + + + + + + + + + + + +```` + ## Color The `Color` (`string`) parameter controls the color of the labels. It accepts **CSS**, **HEX** and **RGB** colors. diff --git a/components/gauges/circular/labels.md b/components/gauges/circular/labels.md index d5eca523e7..5551dc9714 100644 --- a/components/gauges/circular/labels.md +++ b/components/gauges/circular/labels.md @@ -13,13 +13,10 @@ position: 15 You can customize the appearance of the labels rendered on the [scale](slug:circular-gauge-scale) of the Circular Gauge by using the ``, child tag of the ``, and the parameters it exposes: * [Format](#format) - * [Center Template](#center-template) - +* [Position](#position) * [Color](#color) - * [Visible](#visible) - * [Additional Customization](#additional-customization) ## Format @@ -91,6 +88,42 @@ The center template allows you to take control of the rendering of the central s ```` +## Position + +The `Position` parameter is of enum type `CircularGaugeScaleLabelsPosition` and determines whether the Gauge labels are on the inside (default) or outside of the Gauge graphic. Labels on the inside allow for a visually larger component on the same available space. + +>caption Setting Circular Gauge label position + +````RAZOR + + + + + + + + + + + + + + + + + + + + + + + + + +```` + ## Color The `Color` (`string`) parameter controls the color of the labels. It accepts **CSS**, **HEX** and **RGB** colors. diff --git a/components/grid/editing/incell.md b/components/grid/editing/incell.md index 570f0a1c36..b4dd5155e2 100644 --- a/components/grid/editing/incell.md +++ b/components/grid/editing/incell.md @@ -54,6 +54,17 @@ In in-cell edit mode, the `OnAdd` and `OnCreate` events fire immediately one aft The above algorithm is different from [inline](slug:grid-editing-inline) and [popup](slug:grid-editing-popup) editing where new rows are only added to the data source after users populate them with valid values. +## New Row Position + +You can control whether a newly added item appears at the top or bottom of the Grid. Use the `NewRowPosition` parameter to specify the position. + +The `NewRowPosition` parameter accepts values from the `GridNewRowPosition` enum: + +- `Top` (default)—Inserts the new item at the top of the view. +- `Bottom`—Inserts the new item at the bottom of the view. + +For a complete example of how this feature works, see the following [example](slug:grid-editing-incell#basic). + ## Integration with Other Features Here is how the component behaves when the user tries to use add and edit operations together with other component features. Also check the [common information on this topic for all edit modes](slug:grid-editing-overview#integration-with-other-features). @@ -72,6 +83,8 @@ This section explains what happens when the user tries to perform another data o * If the validation is satisfied, then editing completes and the component fires `OnUpdate`. * If the validation is not satisfied, then editing aborts and the component fires `OnCancel`. +Deleting items that are currently in edit mode [fires `OnDelete` with a cloned data item instance](slug:grid-editing-overview#delete-operations). + ### Selection To enable [row selection](slug:grid-selection-row) with in-cell edit mode, use a [checkbox column](slug:components/grid/columns/checkbox). More information on that can be read in the [Row Selection](slug:grid-selection-row#selection-and-editing-modes) article. diff --git a/components/grid/editing/inline.md b/components/grid/editing/inline.md index 047e99ba57..a8022b2902 100644 --- a/components/grid/editing/inline.md +++ b/components/grid/editing/inline.md @@ -37,7 +37,16 @@ Inline add, edit, and delete operations use the following [command buttons](slug In inline edit mode, the Grid commands execute row by row and the corresponding [Grid events](slug:grid-editing-overview#events) also fire row by row. This is similar to [popup editing](slug:grid-editing-popup) and unlike [in-cell editing](slug:grid-editing-incell), where commands and events relate to cells. -When validation is not satisfied, clicking the **Save**, **Delete** or **Add** command buttons has no effect, but users can still navigate between all input components in the row to complete the editing. +When validation is not satisfied, clicking the **Save**, **Delete** or **Add** command buttons have no effect, but users can still navigate between all input components in the row to complete the editing. + +## New Row Position + +You can control whether a newly added item appears at the top or bottom of the Grid. Use the `NewRowPosition` parameter to specify the position. + +The `NewRowPosition` parameter accepts values from the `GridNewRowPosition` enum: + +- `Top` (default)—Inserts the new item at the top of the view. +- `Bottom`—Inserts the new item at the bottom of the view. ## Integration with Other Features @@ -54,6 +63,8 @@ This section explains what happens when the component is already in add or edit If the component is already in add or edit mode, and the user tries to perform another data operation, then editing aborts and the component fires `OnCancel`. +Deleting items that are currently in edit mode [fires `OnDelete` with a cloned data item instance](slug:grid-editing-overview#delete-operations). + ## Examples ### Basic diff --git a/components/grid/editing/overview.md b/components/grid/editing/overview.md index ead20360ca..02f76a10e7 100644 --- a/components/grid/editing/overview.md +++ b/components/grid/editing/overview.md @@ -35,6 +35,7 @@ The Grid CRUD operations rely on the following algorithm: Adding or editing rows in the Grid sets the following requirements on the Grid model: * The Grid model class must have a parameterless constructor. Otherwise, use the [Grid `OnModelInit` event](slug:grid-events#onmodelinit) to provide a data item instance [when the Grid needs to create one](#item-instances). Optinally, you can also [set some default values](slug://grid-kb-default-value-for-new-row). +* There must be a non-editable property that serves as a unique identifier. * All editable properties must be `public` and have setters. These properties must not be `readonly`. * All complex properties used in the Grid must be instantiated in the [Grid `OnModelInit` event](slug:grid-events#onmodelinit). * Self-referencing or inherited properties must not cause `StackOverflowException` or `AmbiguousMatchException` during [programmatic model instance creation](#item-instances). @@ -79,6 +80,8 @@ Delete operations provide the same user experience in all Grid edit modes and re Delete operations can work even if the Grid `EditMode` parameter value is `None`. +If the Grid contains Delete command buttons that display and operate in edit mode, these buttons will fire the [`OnDelete` event](#events) with a [cloned data item instance](#item-instances) in the [event argument](#gridcommandeventargs). To find the original data item in the Grid data source, use the item ID or [override the `Equals()` method of the Grid model class](slug://grid-kb-editing-in-hierarchy). + >tip See the delete operations in action in the complete examples for Grid [inline](slug:grid-editing-inline#examples), [in-cell](slug:grid-editing-incell#examples), and [popup](slug:grid-editing-popup#examples) editing. Also check how to [customize the Delete Confirmation Dialog](slug:grid-kb-customize-delete-confirmation-dialog). ## Commands @@ -97,7 +100,9 @@ Users execute commands in the following ways: * By clicking on editable cells in [in-cell edit mode](slug:grid-editing-incell) and then anywhere else on the page. * By using the [Grid keyboard navigation](https://demos.telerik.com/blazor-ui/grid/keyboard-navigation). -Command buttons can only reside in a [Grid Command Column](slug:components/grid/columns/command) or the [Grid ToolBar](slug:components/grid/features/toolbar). You can also [trigger add and edit operations programmatically](slug:grid-kb-add-edit-state) from anywhere on the web page through the [Grid State](slug:grid-state). +Command buttons can only reside in a [Grid Command Column](slug:components/grid/columns/command) or the [Grid ToolBar](slug:components/grid/features/toolbar). Each command button in the command column is visible only in display mode or only in edit mode, depending on the button's `ShowInEdit` boolean parameter value. + +You can also [trigger add and edit operations programmatically](slug:grid-kb-add-edit-state) from anywhere on the web page through the [Grid State](slug:grid-state). ## Events @@ -110,7 +115,7 @@ The following table describes the Grid events, which are related to adding, dele | `OnAdd` | No | Fires on `Add` [command button](slug://components/grid/columns/command) click, before the Grid enters add mode. This event preceeds `OnCreate` or `OnCancel`. | [New](#item-instances) | Grid remains in read mode. | | `OnCancel` | No | Fires on `Cancel` command invocation. | [New or cloned](#item-instances) | Grid remains in add or edit mode. | | `OnCreate` | To add new items. | Fires on `Save` command invocation for new items. This event succeeds `OnAdd`. | [New](#item-instances) | Grid remains in add mode. | -| `OnDelete` | To [delete items](#delete-operations). | Fires on `Delete` command button click. | Original | Grid won't rebind. Deletion depends on the app itself. | +| `OnDelete` | To [delete items](#delete-operations). | Fires on `Delete` command button click. | [Original or cloned](#item-instances) | Grid won't rebind. Deletion depends on the app itself. | | `OnEdit` | No | Fires on `Edit` command invocation, before the Grid actually enters edit mode. This event preceeds `OnUpdate` or `OnCancel`. | Original | Grid remains in read mode. | | `OnModelInit` | [Depends on the Grid model type](slug:grid-events#onmodelinit) | Fires when the Grid requires a [new model instance](#item-instances), which is immediately before `OnAdd` or immediately after `OnEdit`.
Use this event when the Grid model type is an [interface, abstract class, or has no parameterless constructor](slug:grid-events#onmodelinit). | No event arguments | Not cancellable | | `OnUpdate` | To edit existing items. | Fires on `Save` command invocation for existing items. This event succeeds `OnEdit`. | [Cloned](#item-instances) | Grid remains in edit mode. | @@ -200,6 +205,32 @@ When editing a master row in a [hierarchy Grid](slug://components/grid/features/ Learn more integration details for the [inline](slug:grid-editing-inline#integration-with-other-features) and [in-cell](slug:grid-editing-incell#integration-with-other-features) edit modes. + +## New Row Position + +You can control whether a newly added item appears at the top or bottom of the Grid. Use the [`NewRowPosition`](https://www.telerik.com/blazor-ui/documentation/api/telerik.blazor.gridnewrowposition) parameter to specify the position. This parameter does not affect Popup edit mode, which always displays a dialog for new items. + +This configuration is available in InCell and Inline edit modes. For more details, see the [InCell Editing](slug:grid-editing-incell#new-row-position) and [Inline Editing](slug:grid-editing-inline#new-row-position) articles. + +> When you set `NewRowPosition` to `Bottom`, add the new item at the end of your data collection in the `OnCreate` event handler. When set to `Top`, insert the new item at the beginning of the collection. This ensures the new row appears in the correct position in the view after successfully creating the new record. + +>caption Example of adding a new item to the Grid based on the `NewRowPosition` value + +
+````C# +private void OnCreate(GridCommandEventArgs args) +{ + if (NewRowPosition == GridNewRowPosition.Bottom) + { + dataCollection.Add(newItem); + } + else // Top + { + dataCollection.Insert(0, newItem); + } +} +```` + ## Examples See Grid CRUD operations in action at: diff --git a/components/grid/highlighting.md b/components/grid/highlighting.md new file mode 100644 index 0000000000..9684f99fdd --- /dev/null +++ b/components/grid/highlighting.md @@ -0,0 +1,116 @@ +--- +title: Highlighting +page_title: Grid Highlighting +slug: grid-highlighting +description: Highlight rows and cells in the Telerik Blazor Grid to draw attention to important data. +tags: telerik,blazor,grid,highlight,highlighting +published: true +position: 40 +--- + +# Blazor Grid Highlighting + +The Telerik Blazor Grid enables you to highlight rows and cells programmatically. Use highlighting to draw attention to important data, indicate status, or visually group related records. Highlighting does not require user interaction and is fully controlled by the application logic. + +## Key Features + +* Highlight entire rows by providing a list of data items. +* Highlight individual cells by specifying the data item and column. +* Combine row and cell highlighting. +* Highlighting uses a visual style similar to selection, but does not affect selection state or user interaction. + +To see the Grid highlighting in action, check the below [example](#example). + +## API Reference + +The Grid highlighting feature exposes the following parameters: + +- `HighlightedItems`—Highlight entire rows by providing the data items to highlight. The list must contain references to items from the grid's data source, not new instances. +- `HighlightedCells`—Highlight individual cells by specifying both the data item and the column field. Both values must match the Grid data and column definitions. + +See [Grid Highlighting API Reference](slug:telerik.blazor.components.HighlightedCellDescriptor) for details about these parameters and the `GridHighlightedCellDescriptor` type. + +## Example + +>caption Example of highlighting rows and cells in the Blazor Grid + +````RAZOR + + + + + + + + + + + +@code { + private List GridData { get; set; } = new(); + private List HighlightedItems { get; set; } = new(); + private List HighlightedCells { get; set; } = new(); + + protected override void OnInitialized() + { + GenerateData(); + SetHighlight(); + } + + private void SetHighlight() + { + // Highlight 5 items starting from the 3rd item in the data list + HighlightedItems = GridData.Skip(2).Take(5).ToList(); + + // Highlight specific cells: the ProductName of the first item and Discounted of the second item + HighlightedCells = new List + { + new GridHighlightedCellDescriptor + { + ColumnField = nameof(Product.ProductName), + DataItem = GridData[0] + }, + new GridHighlightedCellDescriptor + { + ColumnField = nameof(Product.Discontinued), + DataItem = GridData[1] + } + }; + } + + private void GenerateData() + { + var random = new Random(); + for (int i = 1; i <= 20; i++) + { + GridData.Add(new Product + { + ProductId = i, + ProductName = $"Product {i}", + UnitPrice = Math.Round(random.NextDouble() * 100, 2), + UnitsInStock = random.Next(1, 100), + CreatedAt = DateTime.Now.AddDays(-random.Next(1, 100)), + Discontinued = i % 5 == 0 + }); + } + } + + public class Product + { + public int ProductId { get; set; } + public string ProductName { get; set; } = string.Empty; + public double UnitPrice { get; set; } + public int UnitsInStock { get; set; } + public DateTime CreatedAt { get; set; } + public bool Discontinued { get; set; } + } +} +```` + +## See Also + +* [Grid Selection](slug:grid-selection-overview) +* [Highlighting API Reference](slug:telerik.blazor.components.HighlightedCellDescriptor) \ No newline at end of file diff --git a/components/grid/overview.md b/components/grid/overview.md index 1fdb9de86b..ff65765d96 100644 --- a/components/grid/overview.md +++ b/components/grid/overview.md @@ -5,6 +5,7 @@ description: The Blazor Grid provides a comprehensive set of ready-to-use featur slug: grid-overview tags: telerik,blazor,grid,datagrid,overview published: True +hideCta: True position: 0 --- @@ -12,6 +13,8 @@ position: 0 This article provides a quick introduction to get your first Blazor data grid component up and running in a few seconds. There is a video tutorial and a list of the key features. +ninja-iconTired of reading docs? With our new AI Coding Assistants, you can add, configure, and troubleshoot Telerik UI for Blazor components—right inside your favorite AI-powered IDE: Visual Studio, VS Code, Cursor, and more. Start building faster, smarter, and with contextual intelligence powered by our docs/APIs:Try AI Assistants + The Telerik Blazor Data Grid provides a comprehensive set of ready-to-use features that cover everything from paging, sorting, filtering, editing, and grouping to row virtualization, optimized data reading, keyboard navigation, and accessibility support. The Telerik Blazor grid is built on native Blazor from the ground up, by a company with a long history of making enterprise-ready Grids. This results in a highly customizable Grid that delivers lighting fast performance. @@ -151,6 +154,7 @@ The Grid supports custom content in various parts of the component such as data * [Drag and drop rows](slug:grid-drag-drop-overview)—move rows in a Grid or between different Grids. * [Loading animation](slug:grid-loading)—show a loading animation to improve user experience during long data operations. * Scrolling—the Grid will show standard scrollbars automatically if the data does not fit the current component width and height. +* [Highlighting](slug:grid-highlighting)—highlight rows or cells programmatically to draw attention to important data. ## Grid Parameters diff --git a/components/grid/selection/cells.md b/components/grid/selection/cells.md index d57d3fd55e..d90f54bdbe 100644 --- a/components/grid/selection/cells.md +++ b/components/grid/selection/cells.md @@ -237,3 +237,4 @@ When using [Grid templates](slug:components/grid/features/templates) with cell s * [Live Demo: Grid Cell Selection](https://demos.telerik.com/blazor-ui/grid/cell-selection) * [Blazor Grid](slug:grid-overview) +* [Grid Highlighting](slug:grid-highlighting) diff --git a/components/grid/selection/rows.md b/components/grid/selection/rows.md index 567060fce3..ed3fcef7a0 100644 --- a/components/grid/selection/rows.md +++ b/components/grid/selection/rows.md @@ -213,3 +213,4 @@ The Grid clears the `SelectedItems` collection when the user drags and drops sel * [Live Demo: Grid Row Selection](https://demos.telerik.com/blazor-ui/grid/row-selection) * [Blazor Grid](slug:grid-overview) +* [Grid Highlighting](slug:grid-highlighting) diff --git a/components/grid/state.md b/components/grid/state.md index 049bc989f1..fb6c7f7b6d 100644 --- a/components/grid/state.md +++ b/components/grid/state.md @@ -43,7 +43,7 @@ The Grid state is a generic [class `GridState`](slug:Telerik.Blazor.Compo | `SelectedItems` | `ICollection` | The currently [selected data item(s)](slug:grid-selection-overview). | | `Skip` | `int?` | The number of scrolled data items when using [virtual row scrolling](slug:components/grid/virtual-scrolling). In other words, this is the number of rows above the currently visible ones. | | `SortDescriptors` | `ICollection` | The currently applied [sorts](slug:components/grid/features/sorting). | -| `TableWidth` | `string` | The sum of all visible column widths. This property changes together with `ColumnStates`. The `OnStateChanged` event does not fire separately for it. | +| `TableWidth` | `string` | The sum of all visible column widths. The initial value is always `null` regardless of the column configuration. The `TableWidth` value changes during column resizing together with `ColumnStates` and the`OnStateChanged` event does not fire separately for it. When you resize a column programmatically, and all other columns already have widths, you must update the `TableWidth` too, otherwise the other columns will resize unexpectedly. | \* `TItem` is the Grid model type. diff --git a/components/grid/templates/editor.md b/components/grid/templates/editor.md index 88c46218e8..d7e769dbf2 100644 --- a/components/grid/templates/editor.md +++ b/components/grid/templates/editor.md @@ -20,12 +20,13 @@ If you need more complex logic inside the editor template, compared to simple da >tip The Editor Template works in all edit modes (Inline, Popup, InCell). Before using it with InCell mode, review the [pertinent notes](slug:grid-editing-incell#editor-template). -When an input receives an `EditContext` (usually comes down as a cascading parameter), the framework also requires a `ValueExpression`. If you use two-way binding (the `@bind-Value` syntax), the `ValueExpression` is deducted from there. However, if you use only the `Value` property, you have to pass the `ValueExpression` yourself. This is a lambda expression that tells the framework what field in the model to update. The following sample demonstrates how to achieve that. You can also check the [Requires a value for ValueExpression](slug://common-kb-requires-valueexpression) knowledge base article for more details. +When an input receives an `EditContext` (usually as a cascading parameter), the framework also requires a `ValueExpression`. If you use two-way binding (the `@bind-Value` syntax), the `ValueExpression` is deducted from there. However, if you use only the `Value` parameter, you have to pass the `ValueExpression` explicitly. This is a lambda expression that tells the framework what property of the model to use for validation. The following sample demonstrates how to achieve that. You can also check the [Requires a value for ValueExpression](slug:common-kb-requires-valueexpression) knowledge base article for more details.
````RAZOR @@ -33,7 +34,6 @@ When an input receives an `EditContext` (usually comes down as a cascading param @* Applies to the other input type components as well *@ ```` - **In this article:** * [Notes](#notes) @@ -46,39 +46,30 @@ When an input receives an `EditContext` (usually comes down as a cascading param * @[template](/_contentTemplates/common/inputs.md#edit-debouncedelay) -* The Grid row creates an `EditContext` and passes it to the `EditorTemplate`. You can read more about it in the [**Notes** section of the Editing Overview](slug:grid-editing-overview#notes) article. - * We recommend casting the Editor Template context to your model and storing it in a local or a dedicated global variable. Do not share a global variable within multiple templates, like column (cell) template and editor template. Variable sharing can lead to unexpected behavior. -* Direct casting of the `context` can make the data binding not work properly. - - ->caption Not recommended: direct casting. Binding does not work properly. +* Direct casting of the `context` can make two-way data binding not work properly. -
+>caption Not recommended: direct casting with two-way parameter binding -````RAZOR +````RAZOR.skip-repl ```` ->caption Recommended: cast the context to your model type and store it in a variable. Binding works as expected. +>caption Recommended: cast the context in advance
````RAZOR @{ - EditedProduct = context as Product; + var editProduct = (Product)context; - + } - -@code{ - private Product EditedProduct { get; set; } -} ```` ## Examples diff --git a/components/listview/overview.md b/components/listview/overview.md index 1dc3d28d4b..082bf1a2f7 100644 --- a/components/listview/overview.md +++ b/components/listview/overview.md @@ -26,50 +26,7 @@ The Blazor >caption ListView in read mode with paging enabled. -````RAZOR -@* Styles would usually go to to the site stylesheet, and you can read more details about -the rest of the features the component provides further in this article *@ - - - -

Employee List

-
- -
- -@code{ - List ListViewData { get; set; } = Enumerable.Range(1, 25).Select(x => new SampleData - { - Id = x, - Name = $"Name {x}", - Team = $"Team {x % 3}" - }).ToList(); - - public class SampleData - { - public int Id { get; set; } - public string Name { get; set; } - public string Team { get; set; } - } -} - - -```` + ## Templates diff --git a/components/listview/templates.md b/components/listview/templates.md index bc935ad3f5..74da09de89 100644 --- a/components/listview/templates.md +++ b/components/listview/templates.md @@ -23,32 +23,7 @@ This is the main building block of the listview component. You define the layout >caption Item template in the ListView -````RAZOR - - - - -@code{ - List ListViewData { get; set; } = Enumerable.Range(1, 250).Select(x => new SampleData - { - Id = x, - Name = $"Name {x}" - }).ToList(); - - public class SampleData - { - public int Id { get; set; } - public string Name { get; set; } - } -} -```` + >caption The result from the snippet above @@ -63,42 +38,7 @@ This is the template that an item in edit or insert mode renders, instead of its >caption Declaring an edit template in the ListView. Note: The CUD operations are not implemented in this example. -````RAZOR -@* This example showcases a minimal edit template declaration. For more details on -the available commands and the event handlers you need to implement, see the following article: -https://docs.telerik.com/blazor-ui/components/listview/editing -*@ - - - - - Save - - - - Add - - - -@code{ - List ListViewData { get; set; } = Enumerable.Range(1, 250).Select(x => new SampleData - { - Id = x, - Name = $"Name {x}" - }).ToList(); - - public class SampleData - { - public int Id { get; set; } - public string Name { get; set; } - } -} -```` + ## Header Template @@ -106,32 +46,7 @@ This piece of code renders just above the items, but within the main listview wr >caption Header Template in the ListView -````RAZOR -@* The item template is mandatory. You can also add other components in the header template. *@ - - - -

Employees

-
- -
- -@code{ - List ListViewData { get; set; } = Enumerable.Range(1, 250).Select(x => new SampleData - { - Id = x, - Name = $"Name {x}" - }).ToList(); - - public class SampleData - { - public int Id { get; set; } - public string Name { get; set; } - } -} -```` + >caption The result from the code snippet above @@ -144,40 +59,12 @@ This piece of code renders just below the items, but within the main listview wr >caption Footer Template in the ListView -````RAZOR -@* As with the other templates, layout and nice visual distinctions are up to the application *@ - - - - A total of @ListViewData.Count items. - - - - -@code{ - List ListViewData { get; set; } = Enumerable.Range(1, 250).Select(x => new SampleData - { - Id = x, - Name = $"Name {x}" - }).ToList(); - - public class SampleData - { - public int Id { get; set; } - public string Name { get; set; } - } -} -```` + >caption The result from the code snippet above ![listview footer template](images/listview-footer-template.png) - - - ## See Also * [Live Demo: ListView Templates](https://demos.telerik.com/blazor-ui/listview/templates) diff --git a/components/multicolumncombobox/data-bind.md b/components/multicolumncombobox/data-bind.md index fa4826e313..2ccbc89eb6 100644 --- a/components/multicolumncombobox/data-bind.md +++ b/components/multicolumncombobox/data-bind.md @@ -53,7 +53,7 @@ Missing selection is most common when: ## Missing Value or Data -The MultiColumnCombobox component attempts to infer the type of its model and value based on the provided `Data` and initial `Value`. This affects its [object reference](slug:multicolumncombobox-overview#component-reference-and-methods). +The MultiColumnCombobox component attempts to infer the type of its model and value based on the provided `Data` and initial `Value`. This affects its [object reference](slug:common-features-data-binding-overview#component-type). In case you cannot provide either the `Value` or `Data` initially, you need to [set the corresponding types to the `TItem` and `TValue` parameters](slug:common-features-data-binding-overview#component-type). diff --git a/components/multicolumncombobox/overview.md b/components/multicolumncombobox/overview.md index 313e883c97..ea6b2b9cd8 100644 --- a/components/multicolumncombobox/overview.md +++ b/components/multicolumncombobox/overview.md @@ -166,11 +166,7 @@ The MultiColumnComboBox provides the following popup settings: ## Component Reference and Methods -To execute MultiColumnComboBox methods, obtain reference to the component instance via `@ref`. - -The MultiColumnComboBox is a generic component. Its type depends on the type of its model and the type of its `Value`. In case you cannot provide either the `Value` or `Data` initially, you need to [set the corresponding types to the `TItem` and `TValue` parameters](slug:common-features-data-binding-overview#component-type). - -The table below lists the MultiComboBox methods. Also consult the [MultiColumnComboBox API](slug:Telerik.Blazor.Components.TelerikMultiColumnComboBox-2). +Add a reference to the component instance to use the [MultiColumnComboBox's methods](slug:Telerik.Blazor.Components.TelerikMultiColumnComboBox-2). Note that the [MultiColumnComboBox is a generic component](slug:common-features-data-binding-overview#component-type). | Method | Description | | --- | --- | diff --git a/components/multiselect/data-bind.md b/components/multiselect/data-bind.md index 6000ec15c2..d21e142013 100644 --- a/components/multiselect/data-bind.md +++ b/components/multiselect/data-bind.md @@ -128,57 +128,7 @@ To bind the MultiSelect to a model: ## Considerations -The MultiSelect component attempts to infer the type of its model and value based on the provided `Data` and initial `Value`. This affects the way its [reference is obtained](#reference) and what happens [if you can't provide data or a value](#missing-value-or-data). - -### Reference - -The MultiSelect is a generic component and its type depends on the type of its `Data` and `Value`. - -
-````RAZOR String -@*Reference type when binding to a string collection*@ - - - -@code { - private TelerikMultiSelect? MultiSelectRef { get; set; } - - private List MultiSelectValue { get; set; } = new(); - - private List MultiSelectData { get; set; } = new List { "first", "second", "third" }; -} -```` -````RAZOR Model -@*Reference when binding to a model collection*@ - - - -@code { - private TelerikMultiSelect? MultiSelectRef { get; set; } - - private List MultiSelectValue { get; set; } = new(); - - private List MultiSelectData { get; set; } = new List() - { - new MultiSelectItem { Text = "first", Value = 1 }, - new MultiSelectItem { Text = "second", Value = 2 }, - new MultiSelectItem { Text = "third", Value = 3 } - }; - - public class MultiSelectItem - { - public string Text { get; set; } = string.Empty; - - public int Value { get; set; } - } -} -```` +The MultiSelect component attempts to infer the type of its model and value based on the provided `Data` and initial `Value`. This affects the way its [reference is obtained](slug:common-features-data-binding-overview#component-type) and what happens [if you can't provide data or a value](#missing-value-or-data). ### Missing Value Or Data diff --git a/components/multiselect/overview.md b/components/multiselect/overview.md index f95842f7f3..734da0ac2b 100644 --- a/components/multiselect/overview.md +++ b/components/multiselect/overview.md @@ -14,47 +14,63 @@ The
Blaz ## Creating MultiSelect -1. Use the `TelerikMultiSelect` tag to add the component to your razor page. +1. Add the `TelerikMultiSelect` tag to your razor page. +1. [Bind the `Data` parameter to the collection of objects or strings](slug:multiselect-databind) that you want to appear in the dropdown. +1. Set the `TextField` parameter to point to the object property that holds the user-readable value. +1. Set the `ValueField` parameter to point to the object property that holds the data item value. +1. [Bind the `Value` of the component](slug:get-started-value-vs-data-binding#value-binding) to a collection of the same type as the type defined by the `ValueField` parameter. +1. (optional) Configure additional features like `AutoClose`, `Placeholder`, or `ShowClearButton`. -1. Populate the `Data` property with the collection of items that you want to appear in the dropdown. - -1. [Bind the value of the component](slug:get-started-value-vs-data-binding#value-binding) to a collection of the same type as the type defined in the `ValueField` parameter. - -1. (Optional) Enable features like placeholder text, clear button, and AutoClose. - ->caption MultiSelect two-way value binding, main features, and simple [data binding](slug:multiselect-databind) +>caption Basic Blazor MultiSelect two-way value binding, main features, and simple [data binding](slug:multiselect-databind) ````RAZOR -@* Main features and simple data binding for the suggestions and the Value *@ + @bind-Value="@MultiSelectValues" + TextField="@nameof(Country.Name)" + ValueField="@nameof(Country.Id)" + AutoClose="false" + Placeholder="Select Balkan Countries" + ShowClearButton="true"> -@if (Values.Count > 0) + +@if (MultiSelectValues.Count > 0) {
    - @foreach (var item in Values) + @foreach (int countryId in MultiSelectValues) { -
  • @item
  • +
  • Id @countryId, Name @Countries.First(x => x.Id == countryId).Name
  • }
} @code { - List Countries { get; set; } = new List(); - List Values { get; set; } = new List(); + private List Countries { get; set; } = new(); + private List MultiSelectValues { get; set; } = new(); + protected override void OnInitialized() { - Countries.Add("Albania"); - Countries.Add("Bosnia & Herzegovina"); - Countries.Add("Bulgaria"); - Countries.Add("Croatia"); - Countries.Add("Kosovo"); - Countries.Add("North Macedonia"); - Countries.Add("Montenegro"); - Countries.Add("Serbia"); - Countries.Add("Slovenia"); - base.OnInitialized(); + Countries.Add(new(1, "Albania")); + Countries.Add(new(2, "Bosnia and Herzegovina")); + Countries.Add(new(3, "Bulgaria")); + Countries.Add(new(4, "Croatia")); + Countries.Add(new(5, "Greece")); + Countries.Add(new(6, "Kosovo")); + Countries.Add(new(7, "Montenegro")); + Countries.Add(new(8, "Romania")); + Countries.Add(new(9, "Serbia")); + Countries.Add(new(10, "Slovenia")); + Countries.Add(new(11, "Turkey")); + } + + public class Country + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + + public Country(int id, string name) + { + Id = id; + Name = name; + } } } ```` @@ -165,9 +181,8 @@ The MultiSelect provides the following popup settings: ## MultiSelect Reference and Methods -The MultiSelect is a generic component and its type is determined by the type of the model you use as its data source. You can find examples in the [Data Bind - Considerations](slug:multiselect-databind#considerations) article. +Add a reference to the component instance to use the [MultiSelect's methods](slug:Telerik.Blazor.Components.TelerikMultiSelect-2). Note that the [MultiSelect is a generic component](slug:common-features-data-binding-overview#component-type). -Add a reference to the component instance to use the [MultiSelect's methods](slug:Telerik.Blazor.Components.TelerikMultiSelect-2). @[template](/_contentTemplates/dropdowns/methods.md#methods-list) diff --git a/components/notification/overview.md b/components/notification/overview.md index 77274c5f42..1a39f5e5b0 100644 --- a/components/notification/overview.md +++ b/components/notification/overview.md @@ -14,6 +14,8 @@ This article provides information about the
+ ## Creating Blazor Notification 1. Add the `` tag to your razor page. diff --git a/components/panelbar/data-binding/overview.md b/components/panelbar/data-binding/overview.md index b3d735677a..9381161ec3 100644 --- a/components/panelbar/data-binding/overview.md +++ b/components/panelbar/data-binding/overview.md @@ -66,7 +66,7 @@ Each `PanelBarBinding` tag exposes the following properties that refer to item p * ItemsField => Items -* Level - this is used for defining [different bindings for different levels](#multiple-level-bindings). If no level is set, the bindings are taken as default for any level that does not have explicit settings. You should have one `TelerikPanelBarBinding` without a level. +* Level—this is used for defining [custom field bindings](#custom-field-bindings) or [different bindings for different levels](#multiple-level-bindings). If no level is set, the bindings are taken as default for any level that does not have explicit settings. You must have one `TelerikPanelBarBinding` without a level to set the default bindings. >tip There are default values for the field names. If your model names match the defaults, you don't have to define them in the bindings settings. @@ -182,6 +182,15 @@ The following **Example** shows how to define simple binding to match item field ![Blazor PanelBar Data Binding](../images/panelbar-data-binding-basic-example.png) +### Custom Field Bindings + +If you are using custom field names, you must ensure their binding for each level. Otherwise, the PanelBar will not render items where the field bindings are missing. + +For that purpose, you must do either of the following: + +* Add one `TelerikPanelBarBinding` without a level to set the default bindings. +* Add `TelerikPanelBarBinding` for each level where you explicitly set the field bindings to your custom fields. + ### Multiple Level Bindings You can define different binding settings for the different levels of nodes in the PanelBar. With this, the children of a node can consume a different field than their parent, and this may make your application more flexible. If you use [hierarchical data binding](slug:panelbar-data-binding-hierarchical), the children can even use a different field or model from their parent. diff --git a/components/panelbar/templates/header.md b/components/panelbar/templates/header.md index 80a45dd9e4..accc52ec2e 100644 --- a/components/panelbar/templates/header.md +++ b/components/panelbar/templates/header.md @@ -13,9 +13,9 @@ position: 5 You can control and customize the rendering of the header items in the PanelBar by using the `HeaderTemplate`. It provides a `context` object that you can cast to the type that the PanelBar is bound to. -The `HeaderTemplate` of a level is defined under the `PanelBarBinding` tag. +The `HeaderTemplate` of a level is defined under the `PanelBarBinding` tag. Set the `Level` parameter of the `PanelBarBinding` to specify the level the `HeaderTemplate` must be applied to. -If no levels are defined the `HeaderTemplate` will apply to the entire data. +If the `Level` parameter of the `PanelBarBinding` is not set, the `HeaderTemplate` will apply to the entire data. >caption Use HeaderTemplate to customize the rendering of the headers in the PanelBar @@ -26,7 +26,7 @@ If no levels are defined the `HeaderTemplate` will apply to the entire data. - + @{ var item = context as PanelBarItem; @@ -42,8 +42,9 @@ If no levels are defined the `HeaderTemplate` will apply to the entire data. @code { - public List Items { get; set; } - public IEnumerable ExpandedItems { get; set; } = new List(); + private List Items { get; set; } + + private IEnumerable ExpandedItems { get; set; } = new List(); public class PanelBarItem { @@ -60,50 +61,50 @@ If no levels are defined the `HeaderTemplate` will apply to the entire data. List items = new List(); items.Add(new PanelBarItem() - { - Id = 1, - Text = "Project", - ParentId = null, - HasChildren = false, - Icon = SvgIcon.Folder, - Url = "projectURL.url" - }); + { + Id = 1, + Text = "Project", + ParentId = null, + HasChildren = false, + Icon = SvgIcon.Folder, + Url = "projectURL.url" + }); items.Add(new PanelBarItem() - { - Id = 2, - Text = "Implementation", - ParentId = null, - HasChildren = true, - Icon = SvgIcon.Code - }); + { + Id = 2, + Text = "Implementation", + ParentId = null, + HasChildren = true, + Icon = SvgIcon.Code + }); items.Add(new PanelBarItem() - { - Id = 3, - Text = "C#", - ParentId = 2, - HasChildren = false, - Icon = SvgIcon.Cs - }); + { + Id = 3, + Text = "C#", + ParentId = 2, + HasChildren = false, + Icon = SvgIcon.Cs + }); items.Add(new PanelBarItem() - { - Id = 4, - Text = "HTML 5", - ParentId = 2, - HasChildren = false, - Icon = SvgIcon.Html5 - }); + { + Id = 4, + Text = "HTML 5", + ParentId = 2, + HasChildren = false, + Icon = SvgIcon.Html5 + }); items.Add(new PanelBarItem() - { - Id = 5, - Text = "CSS", - ParentId = 2, - HasChildren = false, - Icon = SvgIcon.Css - }); + { + Id = 5, + Text = "CSS", + ParentId = 2, + HasChildren = false, + Icon = SvgIcon.Css + }); return items; } diff --git a/components/pdfviewer/form-filling.md b/components/pdfviewer/form-filling.md new file mode 100644 index 0000000000..5a7c083d9b --- /dev/null +++ b/components/pdfviewer/form-filling.md @@ -0,0 +1,33 @@ +--- +title: Form Filling +page_title: PDFViewer - Form Filling +meta_title: Form Filling | PDFViewer for Blazor +position: 17 +description: "Enable users to fill and submit PDF forms in the Blazor PDFViewer." +tags: telerik,blazor,pdfviewer,form filling,forms +slug: pdfviewer-form-filling +--- + +# Form Filling + +The PDFViewer enables users to fill interactive forms directly in PDF documents. You can display and edit form fields such as text boxes, checkboxes, radio buttons, and dropdowns. + +## Supported Form Fields + +The PDFViewer supports the following form field types: + +* `TextBox` +* `CheckBox` +* `RadioButton` +* `DropdownList` +* `ListBox` +* `Button` + +>caption Edit form fields and download the updated PDF file + + + +## See Also + +* [PDFViewer Overview](slug:pdfviewer-overview) +* [PDFViewer Events](slug:pdfviewer-events) diff --git a/components/pdfviewer/overview.md b/components/pdfviewer/overview.md index 1b62e3f6b3..ff241d91fb 100644 --- a/components/pdfviewer/overview.md +++ b/components/pdfviewer/overview.md @@ -25,31 +25,7 @@ To use a Telerik [Blazor PDF Viewer](https://demos.telerik.com/blazor-ui/pdfview >caption Basic Blazor PDF Viewer -````RAZOR - - - -@code { - private byte[] PdfSource { get; set; } - - private async Task OnPdfDownload(PdfViewerDownloadEventArgs args) - { - args.FileName = "PDF-Viewer-Download"; - } - - protected override void OnInitialized() - { - PdfSource = Convert.FromBase64String(PdfBase64); - - base.OnInitialized(); - } - - private const string PdfBase64 = "JVBERi0xLjEKMSAwIG9iajw8L1R5cGUvQ2F0YWxvZy9QYWdlcyAyIDAgUj4+ZW5kb2JqCjIgMCBvYmo8PC9UeXBlL1BhZ2VzL0tpZHNbMyAwIFJdL0NvdW50IDEvTWVkaWFCb3ggWy00MCAtNjQgMjYwIDgwXSA+PmVuZG9iagozIDAgb2JqPDwvVHlwZS9QYWdlL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjE8PC9UeXBlL0ZvbnQvU3VidHlwZS9UeXBlMS9CYXNlRm9udC9BcmlhbD4+ID4+ID4+L0NvbnRlbnRzIDQgMCBSPj5lbmRvYmoKNCAwIG9iajw8L0xlbmd0aCA1OT4+CnN0cmVhbQpCVAovRjEgMTggVGYKMCAwIFRkCihUZWxlcmlrIFBkZlZpZXdlciBmb3IgQmxhem9yKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA1CjAwMDAwMDAwMDAgNjU1MzUgZgowMDAwMDAwMDIxIDAwMDAwIG4KMDAwMDAwMDA4NiAwMDAwMCBuCjAwMDAwMDAxOTUgMDAwMDAgbgowMDAwMDAwNDkwIDAwMDAwIG4KdHJhaWxlciA8PCAgL1Jvb3QgMSAwIFIgL1NpemUgNSA+PgpzdGFydHhyZWYKNjA5CiUlRU9G"; -} -```` - + ## Toolbar @@ -63,6 +39,10 @@ The [PdfViewer toolbar can render built-in and custom tools](slug:pdfviewer-tool The PdfViewer provides a built-in option for creating and editing annotations. Explore the [available annotation types and how to work with them](slug:pdfviewer-annotations). +## Form Filling + +The PdfViewer supports interactive form filling in PDF documents. You can display and edit form fields such as text boxes, checkboxes, radio buttons, and dropdowns. For more details and examples, see the [Form Filling documentation](slug:pdfviewer-form-filling). + ## Large File Support In Blazor **Server** apps, the PDF Viewer uses the **SignalR WebSocket** to: @@ -81,6 +61,7 @@ The table below lists the PDF Viewer parameters. Also check the [PDF Viewer API | Parameter | Type and Default Value | Description | | --- | --- | --- | +| `AnnotationMode` | `PdfViewerAnnotationMode`
(`Disable`) | Specifies how the PDFViewer handles [form fields](slug:pdfviewer-form-filling) in the loaded document. | | `Class` | `string` | An additional CSS class for the `
` element. Use it to [customize the component styles and override the theme](slug:themes-override). | | `Data` | `byte[]` | The source of the currently displayed PDF file. | | `EnableLoaderContainer` | `bool`
(`true`) | Determines if the PDF Viewer will show a loading animation during opening, downloading or zooming a PDF file. | @@ -100,6 +81,7 @@ The PdfViewer exposes methods for programmatic operation. To use them, define a | --- | --- | | `Print` | Prints the loaded PDF document as an alternative to the [built-in Print button in the PDF Viewer toolbar](slug:pdfviewer-toolbar#built-in-tools). | | `Rebind` | Refreshes the PDF Viewer and ensures it is displaying the latest file `Data`. [`Rebind` is necessary when the Blazor framework cannot re-render components automatically](slug:common-features-data-binding-overview#refresh-data). | +| `GetFileAsync` | Asynchronously retrieves the current PDF file data as a byte array, including any annotations or form filling changes. Returns a `Task`. Returns `null` if no document is loaded. | >caption PDF Viewer reference and method usage diff --git a/components/scheduler/recurrence.md b/components/scheduler/recurrence.md index 520933e4e6..e1f09181fe 100644 --- a/components/scheduler/recurrence.md +++ b/components/scheduler/recurrence.md @@ -30,7 +30,7 @@ You can also [define custom property names](slug:scheduler-appointments-databind * `RecurrenceExceptionsField` * `RecurrenceIdField` -A single Scheduler data item defines one series of recurring appointments. Set the `RecurrenceRule` value, according to the [RFC5545 standard](https://tools.ietf.org/html/rfc5545#section-3.3.10). Then, if exceptions to the recurrence rule exist: +A single Scheduler data item defines one series of recurring appointments. Set the `RecurrenceRule` value, according to the [RFC5545 standard](https://tools.ietf.org/html/rfc5545#section-3.3.10), except for a [known discrepancy with extra hyphens in `UNTIL`](https://feedback.telerik.com/blazor/1529000-recurrencerule-does-not-support-the-rfc5545-date-format-like-20210722t000000). Then, if exceptions to the recurrence rule exist: * Each exception must be a separate data item. * The `RecurrenceId` property of each exception must be equal to `Id` value of the recurring appointment. @@ -213,7 +213,6 @@ A single Scheduler data item defines one series of recurring appointments. Set t public Appointment() { - var rand = new Random(); Id = Guid.NewGuid(); } } diff --git a/components/speechtotextbutton/appearance.md b/components/speechtotextbutton/appearance.md new file mode 100644 index 0000000000..d5b68ba16c --- /dev/null +++ b/components/speechtotextbutton/appearance.md @@ -0,0 +1,75 @@ +--- +title: Appearance +page_title: SpeechToTextButton Appearance +description: Customize the appearance of the SpeechToTextButton component in Blazor applications. +slug: speechtotextbutton-appearance +tags: blazor, speech recognition, appearance, customization +published: true +position: 2 +--- + +# SpeechToTextButton Appearance + +You can customize the appearance of the SpeechToTextButton component by using its built-in parameters and CSS classes. The component supports the same appearance options as the [Telerik UI for Blazor Button](slug:components/button/overview). + +## Size + +You can increase or decrease the size of the button by setting the `Size` parameter to a member of the `Telerik.Blazor.ThemeConstants.Button.Size` class. + +To review all available values for the `Size` parameter, see the [Button Size API Reference](https://docs.telerik.com/blazor-ui/api/Telerik.Blazor.ThemeConstants.Button.Size.html). + +>caption Example of setting the Button Size + + + +## Fill Mode + +The `FillMode` toggles the background and border of the TelerikSpeechToTextButton. You can set the parameter to a member of the `Telerik.Blazor.ThemeConstants.Button.FillMode` class. + +To review all available values for the `FillMode` parameter, see the [Button FillMode API Reference](https://docs.telerik.com/blazor-ui/api/Telerik.Blazor.ThemeConstants.Button.FillMode.html). + +>caption Example of setting the FillMode + + + +## Theme Color + +The color of the button is controlled through the `ThemeColor` parameter. You can set it to a member of the `Telerik.Blazor.ThemeConstants.Button.ThemeColor` class. + +To review all available values for the `ThemeColor` parameter, see the [Button ThemeColor API Reference](https://docs.telerik.com/blazor-ui/api/Telerik.Blazor.ThemeConstants.Button.ThemeColor.html). + +>caption Example of setting the ThemeColor + + + +## Rounded + +The `Rounded` parameter applies the border-radius CSS rule to the button to achieve curving of the edges. You can set it to a member of the `Telerik.Blazor.ThemeConstants.Button.Rounded` class. + +To review all available values for the `Rounded` parameter, see the [Button Rounded API Reference](https://docs.telerik.com/blazor-ui/api/Telerik.Blazor.ThemeConstants.Button.Rounded.html). + +>caption Example of Setting the Rounded parameter + + + +## Icon + +Set the `Icon` parameter to display an icon. You can use a predefined [Telerik icon](slug:common-features-icons) or a custom one. + +>caption Example of customizing the default icon + + + +## Custom Styles + +Use the `Class` parameter to apply custom CSS classes. You can further style the button by targeting these classes. + +>caption Example of custom styling + + + +## See Also + +- [SpeechToTextButton Overview](slug:speechtotextbutton-overview) +- [SpeechToTextButton Events](slug:speechtotextbutton-events) +- [SpeechToTextButton Integration](slug:speechtotextbutton-integration) \ No newline at end of file diff --git a/components/speechtotextbutton/events.md b/components/speechtotextbutton/events.md new file mode 100644 index 0000000000..3aaf680246 --- /dev/null +++ b/components/speechtotextbutton/events.md @@ -0,0 +1,45 @@ +--- +title: Events +page_title: SpeechToTextButton Events +description: Learn about the events that the SpeechToTextButton component emits and how to handle them in Blazor applications. +slug: speechtotextbutton-events +tags: blazor, speech recognition, events +published: true +position: 3 +--- + +# SpeechToTextButton Events + +The SpeechToTextButton component emits events that notify you about speech recognition results, errors, and state changes. Use these events to update the UI, display messages, or process the recognized speech. + +## OnResult + +The `OnResult` event fires when the component recognizes speech and produces a result. Use this event to access the recognized phrases, alternatives, and confidence scores. + +To review all available properties of the event arguments for `OnResult`, see the [`SpeechToTextButtonResultEventArgs` API Reference](https://docs.telerik.com/blazor-ui/api/Telerik.Blazor.Components.SpeechToTextButtonResultEventArgs.html). + +>caption Example: Displaying recognized Alternatives and Confidence + + + +## OnStart and OnEnd + +The `OnStart` event fires when speech recognition starts. The `OnEnd` event fires when speech recognition ends. Use these events to update the UI or track the recognition state. + +>caption Example: Indicating listening state + + + +## OnError + +The `OnError` event fires when an error occurs during speech recognition. Use this event to display error messages to the user. + +## OnClick + +The `OnClick` event fires when the user clicks or taps the button. It receives argument of type `MouseEventArgs`. + +## See Also + +- [SpeechToTextButton Overview](slug:speechtotextbutton-overview) +- [SpeechToTextButton Appearance](slug:speechtotextbutton-appearance) +- [SpeechToTextButton Integration](slug:speechtotextbutton-integration) \ No newline at end of file diff --git a/components/speechtotextbutton/integration.md b/components/speechtotextbutton/integration.md new file mode 100644 index 0000000000..a371f57679 --- /dev/null +++ b/components/speechtotextbutton/integration.md @@ -0,0 +1,28 @@ +--- +title: Integration +page_title: Integration +description: Learn how to integrate the SpeechToTextButton component with forms and other components in Blazor applications. +slug: speechtotextbutton-integration +tags: blazor, speech recognition, integration +published: true +position: 4 +--- + +# SpeechToTextButton Integration + +Integrate the SpeechToTextButton component with forms, input fields, and other UI elements to provide voice input capabilities. + +## Binding Recognized Text to an Input Field + +Use the `OnResult` event to update an input field with the recognized text. For example, you can enable users to fill out a feedback form by speaking instead of typing. When the user clicks the SpeechToTextButton, the component captures their speech. It then updates the value of a [TelerikTextArea](slug:textarea-overview) with the recognized text. + +>caption Example of binding the recognized text to an TelerikTextArea + + + +## See Also + +- [AI Model Voice Transcription Intergration](https://github.com/telerik/blazor-ui/tree/master/common/microsoft-extensions-ai-integration/SpeechToTextIntegration) +- [SpeechToTextButton Overview](slug:speechtotextbutton-overview) +- [SpeechToTextButton Events](slug:speechtotextbutton-events) +- [SpeechToTextButton Appearance](slug:speechtotextbutton-appearance) \ No newline at end of file diff --git a/components/speechtotextbutton/overview.md b/components/speechtotextbutton/overview.md new file mode 100644 index 0000000000..e07277703d --- /dev/null +++ b/components/speechtotextbutton/overview.md @@ -0,0 +1,70 @@ +--- +title: Overview +page_title: SpeechToTextButton Overview +description: Learn about the SpeechToTextButton component, its features, and how to use it in Blazor applications. +slug: speechtotextbutton-overview +tags: blazor, speech recognition, button, voice input +published: true +position: 1 +--- + +# Blazor SpeechToTextButton Overview + +The [Blazor SpeechToTextButton component](https://www.telerik.com/blazor-ui/speech-to-text-button) enables speech recognition in Blazor applications. It provides a button that users can select to start and stop speech recognition. The component converts spoken words into text and emits events with the recognized results. + +Use the SpeechToTextButton component to add voice input capabilities to forms, search bars, chat interfaces, and other scenarios that require speech-to-text functionality. + +## Basic Usage + +The following example demonstrates how to add the SpeechToTextButton to a Blazor page and handle the recognition result. + +>caption Example of using the SpeechToTextButton + + + +## Appearance + +You can customize the appearance of the SpeechToTextButton by setting parameters such as `Icon`, and `Class`. For more details and examples, refer to [SpeechToTextButton Appearance](slug:speechtotextbutton-appearance). + +## Events + +The SpeechToTextButton component emits several events that you can handle. For more details, refer to [SpeechToTextButton Events](slug:speechtotextbutton-events). + +## SpeechToTextButton Parameters + +To review all available parameters for the SpeechToTextButton component, see the [SpeechToTextButton API Reference](https://docs.telerik.com/blazor-ui/api/Telerik.Blazor.Components.TelerikSpeechToTextButton#parameters). + +## SpeechToTextButton Reference and Methods + +The SpeechToTextButton component exposes several public methods that you can call from your code. For a full list and details, see the [SpeechToTextButton API Reference](https://docs.telerik.com/blazor-ui/api/Telerik.Blazor.Components.TelerikSpeechToTextButton#methods). + +>caption Example of Calling a Method by Reference + +
+````RAZOR + + +@code { + private async Task StartRecognition() + { + await speechToTextButtonRef.StartAsync(); + } +} +```` + +## Supported Browsers + +The SpeechToTextButton component relies on the Web Speech API. For a list of supported browsers, refer to the [Web Speech API documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API#browser_compatibility). + +## Next Steps + +* [Handle SpeechToTextButton Events](slug:speechtotextbutton-events) +* [SpeechToTextButton Integration](slug:speechtotextbutton-integration) + +## See Also + +* [SpeechToTextButton Live Demo](https://demos.telerik.com/blazor-ui/speechtotextbutton/overview) +* [SpeechToTextButton API Reference](/blazor-ui/api/Telerik.Blazor.Components.TelerikSpeechToTextButton) +* [SpeechToTextButton Events](slug:speechtotextbutton-events) +* [SpeechToTextButton Appearance](slug:speechtotextbutton-appearance) +* [SpeechToTextButton Integration](slug:speechtotextbutton-integration) \ No newline at end of file diff --git a/components/splitter/panes.md b/components/splitter/panes.md index 7d9c002e9e..1a6e54241d 100644 --- a/components/splitter/panes.md +++ b/components/splitter/panes.md @@ -27,6 +27,7 @@ Each Splitter pane is configured individually and offers the following parameter | `Max` | `string` | The maximum size the pane can have in pixels or percentages. When it is reached, the user cannot expand its size further. | | `Min` | `string` | The minimum size the pane can have in pixels or percentages. When it is reached, the user cannot reduce its size further. | | `Resizable` | `bool`
(`true`) | Whether users can resize the pane with a resize handle (splitbar) or the keyboard. Pane resizing always affects two panes. To enable resizing for a specific pane, at least one adjacent pane must be resizable too. | +| `Scrollable` | `bool` | Whether the browser automatically shows scrollbars in panes which do not fit their current content. | | `Size` | `string` | The pane `width` CSS style in [horizontal Splitters](slug:splitter-orientation), or the pane `height` in [vertical Splitters](slug:splitter-orientation). Supports two-way binding. The `Size` must be between the `Min` and `Max` values. See [Pane Size](#pane-size) below for more details on pane dimensions and behavior. | | `Visible` | `bool` | Defines if the pane element and splitbar render or not. When toggled at runtime, the pane's index remains unchanged, unlike when adding a pane with a conditional statement, which appends it at the end. Compare with the `Collapsed` parameter. | @@ -46,7 +47,7 @@ Each Splitter pane is configured individually and offers the following parameter
Collapsible pane with initial size in percentage.
- +

Right Pane

Non-collapsible and scrollable pane with no size. It will take up the remaining space in the component.
@@ -66,10 +67,6 @@ Each Splitter pane is configured individually and offers the following parameter } ```` -## Pane Scrolling - -To make a Splitter pane scrollable without using additional HTML markup or custom CSS styles, set the pane `Class` parameter to `k-scrollable`. See the example above. - ## Pane Dimensions The dimensions of a Splitter pane depend on: diff --git a/components/tabstrip/events.md b/components/tabstrip/events.md index 2753e31fde..4a2ee8522c 100644 --- a/components/tabstrip/events.md +++ b/components/tabstrip/events.md @@ -17,19 +17,21 @@ This article explains the events available in the Telerik TabStrip for Blazor: ## ActiveTabIdChanged -The `ActiveTabIdChanged` event fires when the user changes the active tab. The event handler receives the new tab ID of type `string` as an argument. This event is designed to work with the new [`ActiveTabId` parameter](slug:tabstrip-tabs-collection). +The `ActiveTabIdChanged` event was added in [version 9.0.0](https://www.telerik.com/support/whats-new/blazor-ui/release-history/telerik-ui-for-blazor-9-0-0-(2025-q2)). It fires when the user changes the active tab. The event handler receives the new tab ID of type `string` as an argument. If the `Id` parameter of the `TabStripTab` is not set, the component will generate one automatically. + +The `ActiveTabIdChanged` event is designed to work with the new [`ActiveTabId` parameter](slug:tabstrip-tabs-collection). >caption Handle the tab ID selection changed event ````RAZOR - + First tab content. Click through the tabs. - + Second tab content. - + Third tab content. @@ -37,10 +39,11 @@ The `ActiveTabIdChanged` event fires when the user changes the active tab. The e @Result @code { - private string Result { get; set; } + private string Result { get; set; } = string.Empty; + private void HandleTabIdChange(string tabId) { - Result = $"Current tab ID is {tabId}"; + Result = $"The current tab ID is {tabId}"; } } ```` diff --git a/components/tabstrip/header-template.md b/components/tabstrip/header-template.md index f5006a03ed..3cbc7f4b69 100644 --- a/components/tabstrip/header-template.md +++ b/components/tabstrip/header-template.md @@ -25,36 +25,7 @@ You can define the `HeaderTemplate` with the following configuration in the `Tab >caption The following example showcases the use of a ``, including `TelerikIcons` -````RAZOR -@*A couple of tabs with defferent header configuration.*@ - - - - - - Icon and text - - - This is a tab with a header template including an icon and text. - - - - - - Icon and text 2 - - - This is a tab that has a header template and as well as Title parameter. -
- Title parameter will not be displayed. Only the Header template will be displayed. -
-
- - This is a tab with Title parameter. If you want to use only text in the header, - set the Title parameter only and you can omit the Content tag. - -
-```` + >tip If you only need to add some styling to the TabStripTab header, you can use the `Class` parameter of the `TabStripTab` to define your custom CSS class instead of the Header template. diff --git a/components/tabstrip/overview.md b/components/tabstrip/overview.md index e525a5b253..e6f15ac631 100644 --- a/components/tabstrip/overview.md +++ b/components/tabstrip/overview.md @@ -22,19 +22,7 @@ The Bla >caption TabStrip with specified tab position and a disabled tab -````RAZOR - - - First tab content. - - - Second tab content. This tab is disabled and you cannot select it. - - - Third tab content. - - -```` + ## Active Tab Index diff --git a/components/tabstrip/tabs-collection.md b/components/tabstrip/tabs-collection.md index 77d740a82a..a69aaf9b61 100644 --- a/components/tabstrip/tabs-collection.md +++ b/components/tabstrip/tabs-collection.md @@ -27,7 +27,7 @@ To deactivate all tabs, set the ActiveTabId parameter to `string.Empty`. @{ foreach (var tab in Tabs) { - + @tab.Title @@ -54,11 +54,11 @@ To deactivate all tabs, set the ActiveTabId parameter to `string.Empty`. private string ActiveTabId { get; set; } private List Tabs { get; set; } = new List -{ - new Tab { Id = "home", Title = "🏠 Home", Visible = true, Disabled = false }, - new Tab { Id = "profile", Title = "👤 Profile", Visible = true, Disabled = false }, - new Tab { Id = "settings", Title = "⚙️ Settings", Visible = true, Disabled = false } -}; + { + new Tab { Id = "home", Title = "🏠 Home", Visible = true, Disabled = false }, + new Tab { Id = "profile", Title = "👤 Profile", Visible = true, Disabled = false }, + new Tab { Id = "settings", Title = "⚙️ Settings", Visible = true, Disabled = false } + }; public class Tab { @@ -70,6 +70,11 @@ To deactivate all tabs, set the ActiveTabId parameter to `string.Empty`. } ```` +## Add and Remove Tabs + +If you are iterating through a collection to render the tabs and you need to allow the users to add and remove tabs, you may use the `ActiveTabId` parameter to set the active tab after adding and removing tabs. See details and example in this article: [Add and Remove Tabs](slug:tabstrip-kb-add-remove-tabs). + + ## See Also * [TabStrip Events](slug:tabstrip-events) diff --git a/components/timepicker/events.md b/components/timepicker/events.md index d56375a7fd..4e6af41cf1 100644 --- a/components/timepicker/events.md +++ b/components/timepicker/events.md @@ -22,49 +22,30 @@ This article explains the events available in the Telerik TimePicker for Blazor: The `ValueChanged` event fires upon every change (for example, keystroke) in the input, and upon clicking the `Set` or `Now` buttons in the dropdown. ->caption Handle ValueChanged +The event handler receives the new value as an argument and you must update the component `Value` programmatically for the user changes to take effect. -````RAZOR -@result -
- - - -@code { - string result; - - private void MyValueChangeHandler(DateTime theUserInput) - { - result = string.Format("The user entered: {0}", theUserInput); - } -} -```` - -@[template](/_contentTemplates/common/general-info.md#event-callback-can-be-async) - -@[template](/_contentTemplates/common/issues-and-warnings.md#valuechanged-lambda-required) - ->caption Handle ValueChanged and provide initial value +>caption Handle the TimePicker ValueChanged event ````RAZOR -@result +@Result
-model value: @thePickerValue +TimePicker Value: @TimePickerValue
- + + @code { - string result; + private string Result { get; set; } = string.Empty; - DateTime thePickerValue { get; set; } = DateTime.Now; + private DateTime TimePickerValue { get; set; } = DateTime.Now; - private void MyValueChangeHandler(DateTime theUserInput) + private void TimePickerValueChanged(DateTime newValue) { - result = $"The user entered: {theUserInput}"; + Result = $"The user entered: {newValue}"; - //you have to update the model manually because handling the ValueChanged event does not let you use @bind-Value - thePickerValue = theUserInput; + TimePickerValue = newValue; } } ```` @@ -75,22 +56,27 @@ The `OnChange` event represents a user action - confirmation of the current valu The time picker is a generic component, so you must provide either a `Value`, or a type to the `T` parameter of the component. ->caption Handle OnChange +>caption Handle the TimePicker OnChange event ````RAZOR -@result +@Result +
+TimePicker Value: @TimePickerValue
- + + @code { - string result; + private string Result { get; set; } = string.Empty; + + private DateTime TimePickerValue { get; set; } = DateTime.Now; - private void MyOnChangeHandler(object theUserInput) + private void OnTimePickerChange(object currentValue) { - // the handler receives an object that you may need to cast to the type of the component - // if you do not provide a Value, you must provide the Type parameter to the component - result = string.Format("The user entered: {0:HH:mm:ss}", (DateTime)theUserInput); + // Cast the event argument to the actual value type + Result = $"The user entered: {(DateTime)currentValue}"; } } ```` @@ -99,30 +85,6 @@ The time picker is a generic component, so you must provide either a `Value`, or >tip The `OnChange` event is a custom event and does not interfere with bindings, so you can use it together with models and forms. ->caption Handle OnChange and use two-way binding - -````RAZOR -@result -
-model value: @thePickerValue -
- - - -@code { - string result; - - DateTime? thePickerValue { get; set; } = DateTime.Now; - - private void MyOnChangeHandler(object theUserInput) - { - // the handler receives an object that you may need to cast to the type of the component - // if you do not provide a Value, you must provide the Type parameter to the component - result = string.Format("The user entered: {0}", (theUserInput as DateTime?).Value); - } -} -```` - ## OnOpen The `OnOpen` event fires before the TimePicker popup renders. diff --git a/components/timepicker/overview.md b/components/timepicker/overview.md index 9c19148452..d2d0bc8dec 100644 --- a/components/timepicker/overview.md +++ b/components/timepicker/overview.md @@ -88,6 +88,7 @@ The Blazor Time Picker component provides various parameters that allow you to c | `ReadOnly` | `bool` | If set to `true`, the component will be readonly and will not allow user input. The component is not readonly by default and allows user input. | | `Format` | `string`
(`ShortTimePattern`) | The format of the TimePicker's DateInput. The default value depends on `CultureInfo.CurrentCulture`. Read more at [Supported date formats by the DateInput](slug:components/dateinput/supported-formats). Note that format specifiers for non-time portions will be editable only in the input and will not have a representation in the Time Picker dropdown. | | `Id` | `string` | Renders as the `id` attribute on the `` element, so you can attach a `
sign up for a free trial. +If you are new to Telerik UI for Blazor, [sign up for a free trial](https://www.telerik.com/blazor-ui). -The `Telerik.UI.for.Blazor` NuGet package [depends on the `Telerik.Licensing` NuGet package](slug:getting-started/what-you-need#nuget-packages). During project build, the `Telerik.Licensing` package automatically verifies the license key and activates Telerik UI for Blazor in that project. +The `Telerik.UI.for.Blazor` NuGet package [depends on the `Telerik.Licensing` NuGet package](slug:getting-started/what-you-need#nuget-packages). During project build and publish, the `Telerik.Licensing` package automatically verifies the license key and activates Telerik UI for Blazor in that project. -Follow the steps below for [automatic](#automatic-installation) or [manual](#manual-installation) license key installation in your local development environment. To build Telerik Blazor apps in a CI/CD environment, [set up a Telerik license key in CI/CD](slug:deployment-license-key). +Follow the steps below for [automatic](#automatic-installation) or [manual](#manual-installation) license key installation in your local development environment. Then, find out [how to use a Telerik license key during application deployment](slug:deployment-license-key). An [invalid or missing license key results in app build warnings](slug:troubleshooting-license-key-errors) and watermarks in the application UI. @@ -35,7 +35,7 @@ An [invalid or missing license key results in app build warnings](slug:troublesh To download and install your Telerik license key automatically, use either of the following Telerik productivity tools: -* Telerik Control Panel—this tool downloads and installs different Telerik products on your machine. +* [Telerik Control Panel](https://docs.telerik.com/controlpanel/introduction)—this tool downloads and installs different Telerik products on your machine. * [Telerik UI for Blazor Visual Studio extension](slug:getting-started-vs-integration-overview)—this tool adds or upgrades Telerik UI for Blazor in new or existing apps. The above tools download and install a license key for you and make it available for all projects that you develop on your local machine. The license key file and location is specified in the [Manual Installation](#manual-installation) section below. @@ -61,8 +61,44 @@ The new license key includes information about all previous purchases. This proc * If you used the [manual license key installation](#manual-installation), then repeat the same steps. * To [update your license key in CI/CD environments](slug:deployment-license-key), get your new license key and update the environment variable value. +## Using Telerik Packages in Referenced Projects + +Telerik UI for Blazor may be used in a referenced project in a multi-project app, for example: + +* In the **Client** project of a Blazor Web App with **WebAssembly** or **Auto** render mode. +* In a Razor class library (RCL) project, which is used by another web or RCL project. + +In such cases, you may see a yellow banner in the browser, which says "We couldn't verify your license key for Telerik UI for Blazor. Please see the build log for details and resolution steps". + +There are two alternative ways to avoid the warning banner: + +* Set `PrivateAssets="none"` to the Telerik UI for Blazor NuGet package registration tag. This approach is applicable only if the main (startup) app project directly references the project that includes Telerik UI for Blazor. + ````XML.skip-repl + + ```` + +* Reference the `Telerik.Licensing` NuGet package explicitly in the main (startup) app project. This approach is required if you are using Telerik UI for Blazor in a project hierarchy with more than two levels. For example, the main (startup) app project A references a Razor Class Library (RCL) project B, which references RCL project C that includes Telerik UI for Blazor. You can use the same `Telerik.Licensing` version that is referenced by `Telerik.UI.for.Blazor`, or a newer version. + ````XML.skip-repl + + + + ```` + +## Troubleshooting + +Refer to the [Troubleshooting License Key Errors](slug:troubleshooting-license-key-errors) page to find out what license key warnings you may see during application build and what they mean. + ## Frequently Asked Questions +### Where do I need to install a license key? + +A Telerik license key is required on all environments that build or publish Telerik Blazor apps. This includes: + +* Local development environments. See [Automatic Installation](#automatic-installation) and [Manual Installation](#manual-installation) above. +* Environments that perform [deployment, continuous integration or continuous delivery](slug:deployment-license-key) (CI/CD) to run your apps on a test, staging or production environment. Such environments include Azure Pipelines, GitHub Actions, GitLab, Docker, Jenkins, and others. + +>tip A license key is not required on the web server that hosts the already deployed web application. + ### Does the license key expire? Yes, the license key expires at the end of your subscription: @@ -139,8 +175,8 @@ No, versions up to **7.1.0** released prior to February 2025 do not require a li ## Next Steps -* [Set Up the Telerik NuGet Feed](slug:installation/nuget) * [Install License Key in CI/CD Environment](slug:deployment-license-key) +* [Set Up the Telerik NuGet Feed](slug:installation/nuget) ## See Also diff --git a/installation/nuget.md b/installation/nuget.md index 21b3ab31e8..fa85d0039f 100644 --- a/installation/nuget.md +++ b/installation/nuget.md @@ -186,13 +186,7 @@ The firewall must allow some of the requests to be redirected from `nuget.teleri ## Obsolete Telerik NuGet URL -In addition to feed URL `https://nuget.telerik.com/v3/index.json`, there is also an obsolete NuGet v2 server at `https://nuget.telerik.com/nuget`, which is no longer recommended. - -> The NuGet v2 server at `https://nuget.telerik.com/nuget` will be sunset in November 2024. -> -> The new v3 protocol offers faster package searches and restores, improved security, and more reliable infrastructure. -> -> To redirect your feed to the NuGet v3 protocol, all you have to do is to change your NuGet package source URL to `https://nuget.telerik.com/v3/index.json`. +The NuGet v2 server at `https://nuget.telerik.com/nuget` was sunset in November 2024 and is no longer available. The v3 protocol offers faster package searches and restores, improved security, and more reliable infrastructure. To redirect your feed to the NuGet v3 protocol, all you have to do is to change your NuGet package source URL to `https://nuget.telerik.com/v3/index.json`. ## Troubleshooting diff --git a/introduction.md b/introduction.md index 7a38236eca..a2e72e4a98 100644 --- a/introduction.md +++ b/introduction.md @@ -83,6 +83,7 @@ You can watch a YouTube playlist of getting started tutorials for Telerik UI for + @@ -222,7 +223,7 @@ To use UI for Blazor commercially, you need to + + @{ + var text = context.ToString(); + } +
+ @text + +
+
+ + +@code { + private string Role { get; set; } + private List AutoCompleteData { get; set; } + private List SourceData { get; set; } = new() + { + "Manager", "Developer", "QA", "Technical Writer", "Support Engineer", "Sales Agent", "Architect", "Designer" + }; + + protected override void OnInitialized() + { + AutoCompleteData = SourceData; + } + + private void OnCheckBoxChangeHandler() + { + AutoCompleteData = new List(SourceData); + } + + private async Task CopyToClipboard(string text) + { + try + { + var isSupported = await JS.InvokeAsync("eval", "!!(navigator.clipboard && navigator.clipboard.writeText)"); + if (isSupported) + { + await JS.InvokeVoidAsync("navigator.clipboard.writeText", text); + } + else + { + Console.WriteLine("Clipboard API not supported in this browser."); + // Optionally show a notification to the user + } + } + catch (Exception ex) + { + Console.Error.WriteLine($"Clipboard copy failed: {ex.Message}"); + // Optionally show a fallback UI message to the user + } + } +} +```` + +## See Also + +* [AutoComplete Documentation](slug:autocomplete-overview) +* [Clipboard API Documentation](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) +* [Blazor JavaScript Interoperability](https://docs.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability) diff --git a/knowledge-base/buttongroup-vertical.md b/knowledge-base/buttongroup-vertical.md index abf628b279..d7a8434c66 100644 --- a/knowledge-base/buttongroup-vertical.md +++ b/knowledge-base/buttongroup-vertical.md @@ -13,12 +13,12 @@ res_type: kb ## Environment - - - - - - + + + + + +
ProductButtonGroup for Blazor
ProductButtonGroup for Blazor
## Description @@ -89,3 +89,7 @@ after ```` + +## See Also + +* [ButtonGroup Overview](slug:buttongroup-overview) diff --git a/knowledge-base/chart-axis-labels-units.md b/knowledge-base/chart-axis-labels-units.md new file mode 100644 index 0000000000..d51dbd8475 --- /dev/null +++ b/knowledge-base/chart-axis-labels-units.md @@ -0,0 +1,112 @@ +--- +title: How to Conditionally Change Axis Labels Units +description: Learn how to format and convert the Y-Axis labels in Charts for Blazor to display inch or metric values based on user preference. +type: how-to +page_title: Customizing Y-Axis Labels in Charts for Blazor +meta_title: Customizing Y-Axis Labels in Charts for Blazor +slug: chart-kb-axis-labels-units +tags: blazor, charts, axis, labels, template +res_type: kb +ticketid: 1690815 +--- + +## Environment + + + + + + + + +
ProductCharts for Blazor
+ +## Description + +I want to format and convert the Y-Axis labels to display either metric (mm) or imperial (inches) values based on user preference. + +## Solution + +Use the [`Template` parameter of the `ChartYAxisLabels`](slug:components/chart/label-template-format) to apply custom formatting and conversion logic. Below is an example implementation: + +````RAZOR + + + + + + + + + + + + + + + + + + + + + + + + + + + + Toggle Units (Currently: @(IsMetric ? "Metric" : "Inches")) + + + + +@code { + private bool IsMetric = true; + private string YAxisLabelTemplate => IsMetric ? "yAxisLabelMetric" : "yAxisLabelInches"; + + private void ToggleUnits() + { + IsMetric = !IsMetric; + } + + public class ModelData + { + public int X { get; set; } + public int Y { get; set; } + } + + public List Series1Data = new() + { + new ModelData() { X = 10, Y = 10 }, + new ModelData() { X = 15, Y = 20 }, + new ModelData() { X = 20, Y = 25 }, + new ModelData() { X = 32, Y = 40 }, + new ModelData() { X = 43, Y = 50 }, + new ModelData() { X = 55, Y = 60 }, + new ModelData() { X = 60, Y = 70 }, + new ModelData() { X = 70, Y = 80 }, + new ModelData() { X = 90, Y = 100 }, + }; +} +```` + +## See Also + +* [Chart Overview](slug:components/chart/overview) +* [ChartYAxisLabels Documentation](slug:components/chart/label-template-format) diff --git a/knowledge-base/chart-bubble-size.md b/knowledge-base/chart-bubble-size.md new file mode 100644 index 0000000000..3204267031 --- /dev/null +++ b/knowledge-base/chart-bubble-size.md @@ -0,0 +1,154 @@ +--- +title: Set Specific Bubble Sizes +description: Learn how to define bubble sizes in different Charts that users can compare more easily. +type: how-to +page_title: How to Set Specific Bubble Sizes +slug: chart-kb-bubble-size +tags: telerik, blazor, chart, bubble +ticketid: 1693133 +res_type: kb +--- + +## Environment + + + + + + + + +
ProductChart for Blazor
+ +## Description + +This KB answers the following questions: + +* How to display smaller bubbles for smaller values in a specific Telerik Blazor Chart instance? +* How to help users compare bubble sizes for different data in different Charts? +* How to display all bubbles with a specific defined `Size` value in certain dimensions? +* How to use fixed bubble sizes instead of relative? + +## Solution + +The [Bubble Chart sets the minimum and maximum bubble sizes automatically](slug:components/chart/types/bubble#bubble-sizing), depending on the Chart dimensions, and the smallest and largest `Size` values in all series in the Chart instance. + +To display comparable bubble sizes in multiple Charts: + +1. Use an additional dummy data item with a `Size` value that matches the largest value in all involved Chart instances. +1. Hide the dummy bubble: + * Set the `Min` or `Max` parameter of `` and ``. + * Position the bubble outside the visible Chart area (viewport). + +>caption Use comparable bubble sizes in several Charts + +````RAZOR +

Hundreds

+ +

An additional bubble with Size 3000 is positioned outside the visible Chart area.

+ + + + + + + + + + + + + + + + + +

Thousands

+ +

The max Size value is 3000. This is the max value in all Charts and series, so there is no need for additional hidden bubbles.

+ + + + + + + + + + + + + + + + + +

All Series in Same Chart

+ +

There is no need for additional hidden bubbles.

+ + + + + + + + + + + + + +@code { + private List SeriesData1 = new List() { + new BubbleModel() { X = 50, Y = 100, Size = 100 }, + new BubbleModel() { X = 150, Y = 200, Size = 200 }, + new BubbleModel() { X = 250, Y = 300, Size = 300 }, + + // Size matches the max Size value in SeriesData2 + new BubbleModel() { X = -100, Y = -100, Size = 3000 } + }; + + private List SeriesData2 = new List() { + new BubbleModel() { X = 100, Y = 50, Size = 1000 }, + new BubbleModel() { X = 200, Y = 150, Size = 2000 }, + new BubbleModel() { X = 300, Y = 250, Size = 3000 } + }; + + public class BubbleModel + { + public int X { get; set; } + public int Y { get; set; } + public int Size { get; set; } + } +} +```` + +## See Also + +* [Bubble Chart](slug:components/chart/types/bubble) diff --git a/knowledge-base/chart-newtonsoft-seialization-settings.md b/knowledge-base/chart-newtonsoft-seialization-settings.md index c33d96f71a..1babd83418 100644 --- a/knowledge-base/chart-newtonsoft-seialization-settings.md +++ b/knowledge-base/chart-newtonsoft-seialization-settings.md @@ -199,3 +199,8 @@ public class ChartDataController : ControllerBase ## Notes The approach used internally by the Chart may change in the future. For example, at the time of writing, the new `System.Net.Http.Json` is not yet ready for use, but it may be used in the future. Thus, the approach described in this article may become unnecessary or wrong. + +## See Also + +* [DataSourceRequest Filters not Working When You Add Reporting or Newtonsoft.Json](slug:common-kb-newtonsoft-breaks-datasourcerequest-serialization) +* [InitMediaQueryWidget Throws JsonException](slug:mediaquery-kb-initmediaquery-jsonexception) diff --git a/knowledge-base/chiplist-add-chip-tooltips.md b/knowledge-base/chiplist-add-chip-tooltips.md new file mode 100644 index 0000000000..0b075650d2 --- /dev/null +++ b/knowledge-base/chiplist-add-chip-tooltips.md @@ -0,0 +1,84 @@ +--- +title: Display Tooltip for Each Chip in a Telerik ChipList +description: Learn how to display tooltips for chips in the Telerik Blazor ChipList component. +type: how-to +page_title: How to Display Tooltips for Chips in Telerik Blazor ChipList +meta_title: How to Display Tooltips for Chips in Telerik Blazor ChipList +slug: chiplist-kb-add-chip-tooltips +tags: blazor, chiplist, tooltip, template +res_type: kb +ticketid: 1691888 +--- + +## Environment + + + + + + + + +
ProductTelerik UI for Blazor ChipList
+ +## Description + +You can display additional information for each chip in the [ChipList](slug:chiplist-overview) by showing a Tooltip. This approach helps you keep the chip text concise while providing more details on hover. + +This article answers the following questions: +- How do you show extra details for chips in a ChipList? +- Can you display a TelerikTooltip for each chip in the ChipList? +- How do you use [`ItemTemplate`](slug:chiplist-templates) in Telerik Blazor ChipList? + +## Solution + +To add tooltips to chips in the ChipList, use the `ItemTemplate` to customize chip rendering and set a tooltip for each chip. Follow these steps: + +1. Add a `Description` property to the model used for the ChipList. +2. Use the `ItemTemplate` to render each chip and set the `title` attribute for a native tooltip. +3. Optionally, use the `TelerikTooltip` component for enhanced tooltip appearance and behavior. + +**Example: Displaying Tooltips for Chips** + +```razor + + +
+ + @context.Text +
+
+
+ + + +@code { + private List ChipListSource { get; set; } = new List + { + new ChipModel + { + Text = "Audio", + Icon = SvgIcon.FileAudio, + Description = "Audio file chip." + }, + new ChipModel + { + Text = "Video", + Icon = SvgIcon.FileVideo, + Description = "Video file chip." + } + }; + + public class ChipModel + { + public string Text { get; set; } + public ISvgIcon? Icon { get; set; } + public string Description { get; set; } + } +} +``` + +## See Also +- [ChipList Overview](slug:chiplist-overview) +- [ChipList Templates](slug:chiplist-templates#item-template) +- [Tooltip Overview](slug:tooltip-overview) \ No newline at end of file diff --git a/knowledge-base/combobox-auto-select-on-blur.md b/knowledge-base/combobox-auto-select-on-blur.md new file mode 100644 index 0000000000..687824178b --- /dev/null +++ b/knowledge-base/combobox-auto-select-on-blur.md @@ -0,0 +1,186 @@ +--- +title: Select Focused Dropdown Item on Tab +description: Learn how to configure the Telerik ComboBox for Blazor to automatically select the first matching item when the user tabs and the component loses focus. +type: how-to +page_title: How to Automatically Select the Focused Dropdown Item on Tab +slug: combobox-kb-autoselect-on-blur +tags: telerik, blazor, combobox, blur, auto-select +res_type: kb +--- + +## Environment + + + + + + + + +
ProductComboBox for Blazor
+ +## Description + +The article asnwers to the following question: + +* How to configure the ComboBox to automatically select the first matching item when the input loses focus (e.g., when the user tabs away or clicks outside)? +* How to auto-select a ComboBox item based on user input when focus is lost? +* Can the ComboBox select a suggested item on blur without pressing Enter? +* How to set the ComboBox value when the user leaves the input field? + +## Solution + +To automatically select the first matching item in the ComboBox when the input loses focus, use a combination of the ComboBox [`OnRead` event](slug:components/combobox/events#onread) and JavaScript interop. The provided example demonstrates how to: + +1. Use the `OnRead` event to filter data and store the first matching item. +2. Attach a JavaScript event handler to detect when the user blurs the ComboBox input. +3. Invoke a .NET method from JavaScript to set the ComboBox value to the first matching item when focus is lost. +4. Update the ComboBox selection programmatically and refresh the UI. + +>caption Auto-select the first matching item on blur + +````RAZOR +@using Telerik.DataSource.Extensions + +@implements IDisposable + +@inject IJSRuntime js + +

ComboBoxFirstItem: @ComboBoxFirstItem?.Text

+ +

Selected value: @ComboBoxValue

+ + + + + + +
+
+ + +@* Move JavaScript to a separate JS file *@ + + +@code { + private DotNetObjectReference<__Main>? DotNetRef { get; set; } + + private List ComboBoxData { get; set; } = new(); + private int ComboBoxValue { get; set; } + private ListItem? ComboBoxFirstItem { get; set; } + + [JSInvokable("OnComboBoxTab")] + public void OnComboBoxTab(string newStringValue) + { + if (ComboBoxFirstItem is not null && ComboBoxFirstItem.Text.ToLowerInvariant().Contains(newStringValue.ToLowerInvariant())) + { + ComboBoxValue = ComboBoxFirstItem.Id; + ComboBoxFirstItem = default; + + StateHasChanged(); + } + } + + private void ComboBoxValueChanged(int newValue) + { + ComboBoxValue = newValue; + ComboBoxFirstItem = default; + } + + private async Task OnComboBoxRead(ReadEventArgs args) + { + var result = await ComboBoxData.ToDataSourceResultAsync(args.Request); + + args.Data = result.Data; + args.Total = result.Total; + + if (args.Request.Filters.Count > 0 && result.Data.Cast().Count() > 0) + { + ComboBoxFirstItem = args.Data.Cast().First(); + } + else + { + ComboBoxFirstItem = default; + } + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await Task.Delay(1); // ensure HTML is ready + await js.InvokeVoidAsync("saveDotNetRef", DotNetRef); + await js.InvokeVoidAsync("attachComboKeyDown", "#combo-1"); + } + + await base.OnAfterRenderAsync(firstRender); + } + + protected override void OnInitialized() + { + DotNetRef = DotNetObjectReference.Create(this); + + for (int i = 1; i <= 24; i++) + { + ComboBoxData.Add(new ListItem() + { + Id = i, + Text = $"Item {i}" + }); + } + } + + public void Dispose() + { + DotNetRef?.Dispose(); + _ = js.InvokeVoidAsync("detachComboKeyDown", "#combo-1"); + } + + public class ListItem + { + public int Id { get; set; } + public string Text { get; set; } = string.Empty; + } +} +```` +## See Also + +* [ComboBox Events](slug:components/combobox/events) diff --git a/knowledge-base/common-extend-inherit-wrap-reuse-telerik-blazor-components.md b/knowledge-base/common-extend-inherit-wrap-reuse-telerik-blazor-components.md index cbe8a4d9a9..765f923496 100644 --- a/knowledge-base/common-extend-inherit-wrap-reuse-telerik-blazor-components.md +++ b/knowledge-base/common-extend-inherit-wrap-reuse-telerik-blazor-components.md @@ -5,7 +5,7 @@ type: how-to page_title: How to Extend, Inherit, or Wrap Telerik Components for Blazor slug: common-kb-component-inheritance tags: telerik, blazor, inheritance -ticketid: 1628856, 1615737, 1604776, 1607228, 1618168 +ticketid: 1628856, 1615737, 1604776, 1607228, 1618168, 1690926, 1694691 res_type: kb --- @@ -45,9 +45,11 @@ On the other hand, neither option allows changing the internal HTML rendering of > Using both options at the same time is not possible and will lead to runtime exceptions, because such a setup will nest a Telerik component in itself. The following JavaScript error will likely occur: `Cannot read properties of null (reading addEventListener)`. +> The development tasks discussed in this article fall outside the Telerik support scope. + ### Wrap Telerik Components -This approach allows you to add additional markup or another components next to the Telerik component. +This approach allows you to add additional markup or other components next to the Telerik component. 1. Add the Telerik component to a separate `.razor` file, for example, `MyReusableComponent.razor`. 1. Implement the desired `MyReusableComponent` parameters and set the required parameters of the Telerik component. @@ -264,6 +266,17 @@ namespace YourAppName.BaseComponents } ```` +## Notes + +If `ReusableComboBox.razor` has a separate `.razor.cs` file, then the [partial class must be defined as generic](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/generic-type-support): + +````C#.skip-repl +public partial class ReusableComboBox : ComponentBase +{ + +} +```` + ## See Also * [Using Base Classes with Razor Components](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-9.0#specify-a-base-class) diff --git a/knowledge-base/common-newtonsoft-breaks-datasourcerequest-serialization.md b/knowledge-base/common-newtonsoft-breaks-datasourcerequest-serialization.md index 59d8705419..a8daa0fde9 100644 --- a/knowledge-base/common-newtonsoft-breaks-datasourcerequest-serialization.md +++ b/knowledge-base/common-newtonsoft-breaks-datasourcerequest-serialization.md @@ -112,3 +112,7 @@ There are three other paths forward you can consider: Telerik supports serialization of the `DataSourceRequest` as part of the [`Telerik.DataSource` package](slug:common-features-datasource-package) (which is used by UI for Blazor) with the `System.Text.Json` serializer only. +## See Also + +* [Chart not Working with Newtonsoft.Json Properties](slug:chart-kb-newtonsoft-seialization-settings) +* [InitMediaQueryWidget Throws JsonException](slug:mediaquery-kb-initmediaquery-jsonexception) diff --git a/knowledge-base/dockmanager-reset-state.md b/knowledge-base/dockmanager-reset-state.md new file mode 100644 index 0000000000..8ee241fb36 --- /dev/null +++ b/knowledge-base/dockmanager-reset-state.md @@ -0,0 +1,134 @@ +--- +title: Reset DockManager State on Button Click in Blazor +description: Learn how to reset the DockManager state in Blazor using a button click and save the default state after the initial render. +type: how-to +page_title: How to Reset DockManager State Dynamically in Blazor +meta_title: Reset DockManager State Dynamically in Blazor +slug: dockmanager-kb-reset-state +tags: dockmanager, blazor, state +res_type: kb +ticketid: 1691957 +--- + +## Environment + + + + + + + +
ProductDockManager for Blazor
+ +## Description + +I want to reset the [DockManager state](slug:dockmanager-state) on a button click. The DockManager currently only resets by reloading the page. I need a solution to reset its state dynamically. + +This knowledge base article also answers the following questions: +- How to reset DockManager layout to its default state? +- How to refresh DockManager without reloading the page? +- How to implement a button to reset DockManager panes? + +## Solution + +To reset the DockManager state dynamically on a button click: + +1. Capture and save the default DockManager state in the [`OnAfterRenderAsync`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.componentbase.onafterrenderasync?view=aspnetcore-9.0) lifecycle method. + +2. Restore the previously saved default state when the button is clicked. + +>caption Reset the DockManager layout to its default state + +````RAZOR +Change something in the DockManager (move, resize, or close panes). Тhen click +Reset Dock State + + + + + + +
    +
  • Fix login bug
  • +
  • Implement dark mode
  • +
  • Refactor API requests
  • +
+
+
+ + +

Upcoming Meetings:

+
    +
  • UI Review - Feb 10
  • +
  • Code Freeze - Feb 15
  • +
  • Final Release - Mar 1
  • +
+
+
+ + +

User A updated Task 1

+

User B commented on Task 2

+

New PR merged for Feature X

+
+
+ + + + +

New messages from Sarah

+

Server maintenance scheduled for Sunday

+
+
+ + +

Enable email notifications

+

Change password

+
+
+
+
+
+
+
+ + + + + +

Live chat with team members

+
+
+ + +

Logs and debugging tools

+
+
+
+
+
+
+ +@code { + private TelerikDockManager? DockManagerRef { get; set; } + private DockManagerState? DefaultState { get; set; } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + DefaultState = DockManagerRef?.GetState(); + } + } + private void ResetDockState() + { + DockManagerRef?.SetState(DefaultState); + } +} +```` + +## See Also + +- [DockManager Overview](slug:dockmanager-overview) +- [DockManager State](slug:dockmanager-state) diff --git a/knowledge-base/filemanager-load-file-data-on-demand.md b/knowledge-base/filemanager-load-file-data-on-demand.md new file mode 100644 index 0000000000..23168a57c2 --- /dev/null +++ b/knowledge-base/filemanager-load-file-data-on-demand.md @@ -0,0 +1,207 @@ +--- +title: Load FileManager File Data on Demand +description: Learn how to load the files from the currently selected folder in the FileManager for better performance. +type: how-to +page_title: How to Load FileManager File Data on Demand +slug: filemanager-kb-load-file-data-on-demand +tags: telerik, blazor, filemanager +ticketid: 1677209, 1665314, 1608943, 1606421, 1566070 +res_type: kb +--- + +## Environment + + + + + + + + +
ProductFileManager for Blazor
+ +## Description + +This KB article answers the following questions: + +* How to load FileManager files on demand? +* How to lazy load the FileManager data? +* How to bind the FileManager only to the files from the currently selected folder? +* How to implement data virtualization for the Telerik FileManager for Blazor? +* How to dynamically add folders and files to the FileManager data? + +## Solution + +1. Subscribe the to the [FileManager `PathChanged` event](slug:filemanager-events#pathchanged). +1. Subscribe to the [FileManager `OnRead` event](slug:filemanager-events#onread). +1. Obtain the [FileManager `@ref` (component instance)](slug:filemanager-overview#reference-and-methods). +1. Call the FileManager `Rebind()` method in the `PathChanged` event handler. +1. Load all folders for the FileManager TreeView and the files from the current folder. Set this collection to [`args.Data` in the `OnRead` handler](slug:common-features-data-binding-onread#event-argument). + +>caption Load FileManager file data on demand using OnRead and PathChanged + +````RAZOR +@using System.IO +@using Telerik.DataSource.Extensions + +

+ Total folder and file count: @FlatData.Count; + Folders loaded: @FoldersLoadedCount; + Files loaded: @FilesLoadedCount +

+ +

Path: @FlatDirectoryPath

+ + + + +@code { + private TelerikFileManager? FileManagerRef { get; set; } + private List FlatData { get; set; } = new List(); + + private readonly string RootPath = string.Empty; + private string FlatDirectoryPath { get; set; } = string.Empty; + + private int FoldersLoadedCount { get; set; } + private int FilesLoadedCount { get; set; } + + private int FolderLevelCount { get; set; } = 5; + private int FilesInFolderMinCount { get; set; } = 3; + private int FilesInFolderMaxCount { get; set; } = 24; + private int FoldersInFolderCount { get; set; } = 3; + private int FolderNameCounter { get; set; } + private readonly List FileExtensions = new() { + ".txt", ".pdf", ".docx", ".xlsx", ".png", ".jpg", ".gif", ".zip", ".css", ".html", ".mp3", ".mpg" + }; + + private void FileManagerPathChanged(string newPath) + { + FlatDirectoryPath = newPath; + + FileManagerRef?.Rebind(); + } + + private async Task OnFileManagerRead(FileManagerReadEventArgs args) + { + var allFoldersAndVisibleFiles = FlatData.Where(x => + // all folders + x.IsDirectory || + // all files if in the root folder + (FlatDirectoryPath == RootPath && x.Path.IndexOf(Path.DirectorySeparatorChar) == x.Path.LastIndexOf(Path.DirectorySeparatorChar)) || + // all files if in another folder + x.Path.Replace(string.Concat(Path.DirectorySeparatorChar, x.Name, x.Extension), string.Empty) == FlatDirectoryPath); + + args.Data = allFoldersAndVisibleFiles; + args.Total = allFoldersAndVisibleFiles.Count(); + + FoldersLoadedCount = allFoldersAndVisibleFiles.Where(x => x.IsDirectory).Count(); + FilesLoadedCount = allFoldersAndVisibleFiles.Count() - FoldersLoadedCount; + + await Task.Delay(1); // simulate async operation + } + + protected override async Task OnInitializedAsync() + { + await Task.CompletedTask; + + FlatDirectoryPath = RootPath; + + FlatData = LoadFlatDataAsync(); + + await base.OnInitializedAsync(); + } + + private List LoadFlatDataAsync() + { + List data = new List(); + + string rootDataPath = string.IsNullOrEmpty(RootPath) ? "/" : RootPath; + + PopulateChildren(data, null, rootDataPath, 1); + + return data; + } + + private void PopulateChildren(List data, string? parentId, string parentPath, int level) + { + var rnd = Random.Shared; + var filesInCurrentFolder = rnd.Next(FilesInFolderMinCount, FilesInFolderMaxCount); + + for (int i = 1; i <= filesInCurrentFolder; i++) + { + string itemId = Guid.NewGuid().ToString(); + string itemExtension = FileExtensions[rnd.Next(0, FileExtensions.Count)]; + string itemName = $"{itemExtension.Substring(1)}-file-{(FolderNameCounter != default ? string.Concat(FolderNameCounter, "-") : string.Empty)}{i}"; + string itemPath = Path.Combine(parentPath, string.Concat(itemName, itemExtension)); + + data.Add(new FlatFileEntry() + { + Id = itemId, + ParentId = parentId, + Name = itemName, + IsDirectory = false, + HasDirectories = false, + DateCreated = DateTime.Now, + DateCreatedUtc = DateTime.Now.ToUniversalTime(), + DateModified = DateTime.Now, + DateModifiedUtc = DateTime.Now.ToUniversalTime(), + Path = itemPath, + Extension = itemExtension, + Size = rnd.Next(1_000, 3_000_000) + }); + } + + if (level < FolderLevelCount) + { + for (int i = 1; i <= FoldersInFolderCount; i++) + { + var itemId = Guid.NewGuid().ToString(); + var itemName = $"folder-{++FolderNameCounter}"; + var itemPath = Path.Combine(parentPath, itemName); + + data.Add(new FlatFileEntry() + { + Id = itemId, + ParentId = parentId, + Name = itemName, + IsDirectory = true, + HasDirectories = level < FolderLevelCount - 1, + DateCreated = DateTime.Now, + DateCreatedUtc = DateTime.Now.ToUniversalTime(), + DateModified = DateTime.Now, + DateModifiedUtc = DateTime.Now.ToUniversalTime(), + Path = itemPath, + Size = rnd.Next(100_000, 10_000_000) + }); + + PopulateChildren(data, itemId, itemPath, level + 1); + } + } + } + + public class FlatFileEntry + { + public string Id { get; set; } = Guid.NewGuid().ToString(); + public string? ParentId { get; set; } + public string Name { get; set; } = string.Empty; + public long Size { get; set; } + public string Path { get; set; } = string.Empty; + public string Extension { get; set; } = string.Empty; + public bool IsDirectory { get; set; } + public bool HasDirectories { get; set; } + public DateTime DateCreated { get; set; } + public DateTime DateCreatedUtc { get; set; } + public DateTime DateModified { get; set; } + public DateTime DateModifiedUtc { get; set; } + } +} +```` + +## See Also + +* [FileManager Events](slug:filemanager-events) diff --git a/knowledge-base/form-fluent-validation-cannot-validate-instances-of-type.md b/knowledge-base/form-fluent-validation-cannot-validate-instances-of-type.md new file mode 100644 index 0000000000..2e16beb57e --- /dev/null +++ b/knowledge-base/form-fluent-validation-cannot-validate-instances-of-type.md @@ -0,0 +1,114 @@ +--- +title: FluentValidation Exception Cannot Validate Instances of Type 'ComponentName' +description: Learn how to troubleshoot and resolve runtime exceptions Cannot Validate Instances of Type 'ComponentName'. This Validator Can only Validate Instances of Type 'ModelClassName' in Blazor apps. +type: troubleshooting +page_title: FluentValidation Exception Cannot Validate Instances of Type 'ComponentName'. This Validator Can only Validate Instances of Type 'ModelClassName' +slug: form-kb-fluent-validation-cannot-validate-instances-of-type +tags: telerik, blazor, form, validation +ticketid: 1689449, 1644783, 1670949, 1672711, 1668735, 1595169, 1539619 +res_type: kb +--- + +## Environment + + + + + + + + +
Product + Form for Blazor,
+ Grid for Blazor +
+ +## Description + +The KB article deals with the following scenarios that throw a runtime exception. It explains what causes the error and how to fix it. + +* A Telerik Form is using a `FluentValidation` validator. A custom input component in a [`FormItem` `Template`](slug:form-formitems-template) crashes the page on value edit. +* An Telerik Grid with inline, incell or popup `EditMode` is using `` for validation. One of the Grid columns has an `` with a custom component that wraps a ``. + +In both cases the exception message that occurs on value edit in the custom component is: + +`Cannot validate instances of type 'ComponentName'. This validator can only validate instances of type 'ModelClassName'` + +In general, the exception may occur when using `FluentValidation` with inputs that are wrapped in custom child components. + +## Cause + +The exception occurs, because the custom child component that holds the input is not receiving the correct `ValueExpression` from the parent component that holds the edit form. + +The issue is not related to or caused by Telerik UI for Blazor. The same behavior can occur with a standard Blazor `EditForm` and `InputText` components. + +## Solution + +1. Define a public parameter of type `Expression>` in the child component. The purpose of this parameter is to receive the correct expression from the parent component. `T` is the value type, which the custom child component is editing. The parameter name must be consistent with the other two related parameter names that deal the two-way value binding: + * `Foo` + * `FooChanged` + * `FooExpression` +1. Pass the validation expression from the parent to the child component. There are two possible options: + * Use two-way binding for the value parameter (`@bind-Foo`) in the parent component. + * Pass the expression explicitly by setting `FooExpression` in the parent component. + +For a full runnable example, refer to [Form Fluent Validation](slug:form-validation#fluent-validation). + +>caption Using ValueExpression for validation in child components + +```RAZOR TextBox.razor +@using System.Linq.Expressions + + + +@code { + [Parameter] + public string Value { get; set; } = string.Empty; + + [Parameter] + public EventCallback ValueChanged { get; set; } + + [Parameter] + public Expression>? ValueExpression { get; set; } + + private async Task TextBoxValueChanged(string newValue) + { + Value = newValue; + + if (ValueChanged.HasDelegate) + { + await ValueChanged.InvokeAsync(newValue); + } + } +} +```` +````RAZOR Home.razor + + +or + + + +@code { + private Person PersonToEdit { get; set; } = new(); + + private void FirstNameChanged(string newValue) + { + PersonToEdit.FirstName = newValue; + } + + public class Person + { + public string FirstName { get; set; } = string.Empty; + } +} +```` + +## See Also + +* [Form Validation](slug:form-validation#fluent-validation) +* [Form Item Templates](slug:form-formitems-template) diff --git a/knowledge-base/gantt-persist-treelist-width-after-refresh.md b/knowledge-base/gantt-persist-treelist-width-after-refresh.md new file mode 100644 index 0000000000..169c7c23a0 --- /dev/null +++ b/knowledge-base/gantt-persist-treelist-width-after-refresh.md @@ -0,0 +1,210 @@ +--- +title: Persist Gantt TreeList after Refresh +description: How to persist the width of the Gantt's treelist after refreshing its data? +type: how-to +page_title: How to Display Model Fields in the Gantt Tooltip? +slug: gantt-kb-persist-treelist-width-after-refresh +tags: gantt, blazor, treelist, width +res_type: kb +ticketid: 1690467 +--- + +## Environment + + + + + + + +
ProductGantt for Blazor
+ +## Description + +If I resize the TreeList part of the Gantt, it gets back to its initial width, after I refresh the Gantt's data. How can I persist the current TreeList width? + +## Solution + +To persist the current TreeList width, follow the steps below: + +1. Bind the `TreeListWidth` parameter of the Gantt to a property. In the example below, the property is named `ListWidth`. +2. In the methods that manipulate the data of the Gantt (`AddRootTask` and `RemoveTask`), use JavaScript interop to invoke a function. The logic in the function gets and returns the current width of the TreeList. +3. Use the current width value to update the value of the `ListWidth` property, before refreshing the data by calling `.Rebind()`. + +````RAZOR +@inject IJSRuntime JS + +Add root task + +Remove Task 1 + + + + + + + + + + + + + + + + + + + + + +@code { + private TelerikGantt? GanttRef { get; set; } + private string ListWidth { get; set; } = "390px"; + private int LastId { get; set; } = 1; + private List GanttData { get; set; } + private GanttView SelectedView { get; set; } = GanttView.Week; + + private async Task AddRootTask() + { + var i = GanttData.Last().Id + 1; + + GanttData.Insert(0, new FlatModel() + { + Id = i, + ParentId = null, + Title = "new task", + PercentComplete = 0, + Start = new DateTime(2021, 7, 5), + End = new DateTime(2021, 7, 15) + }); + + var currentListWidth = await JS.InvokeAsync("getListSize"); + ListWidth = currentListWidth; + + GanttRef?.Rebind(); + } + + private async Task RemoveTask() + { + var taskToRemove = GanttData.FirstOrDefault(x => x.Title == "Task 1"); + + GanttData.Remove(taskToRemove); + + var currentListWidth = await JS.InvokeAsync("getListSize"); + ListWidth = currentListWidth; + + GanttRef?.Rebind(); + } + + + private void UpdateItem(GanttUpdateEventArgs args) + { + var item = args.Item as FlatModel; + + var foundItem = GanttData.FirstOrDefault(i => i.Id.Equals(item.Id)); + + if (foundItem != null) + { + foundItem.Title = item.Title; + foundItem.Start = item.Start; + foundItem.End = item.End; + foundItem.PercentComplete = item.PercentComplete; + } + } + + private void DeleteItem(GanttDeleteEventArgs args) + { + var item = GanttData.FirstOrDefault(i => i.Id.Equals((args.Item as FlatModel).Id)); + + RemoveChildRecursive(item); + } + + private void RemoveChildRecursive(FlatModel item) + { + var children = GanttData.Where(i => item.Id.Equals(i.ParentId)).ToList(); + + foreach (var child in children) + { + RemoveChildRecursive(child); + } + + GanttData.Remove(item); + } + + protected override void OnInitialized() + { + GanttData = new List(); + + for (int i = 1; i < 6; i++) + { + var newItem = new FlatModel() + { + Id = LastId, + Title = "Task " + i.ToString(), + Start = new DateTime(2021, 7, 5 + i), + End = new DateTime(2021, 7, 11 + i), + PercentComplete = Math.Round(Random.Shared.NextDouble(), 2) + }; + + GanttData.Add(newItem); + var parentId = LastId; + LastId++; + + for (int j = 0; j < 5; j++) + { + GanttData.Add(new FlatModel() + { + Id = LastId, + ParentId = parentId, + Title = " Task " + i + " : " + j.ToString(), + Start = new DateTime(2021, 7, 5 + j), + End = new DateTime(2021, 7, 6 + i + j), + PercentComplete = Math.Round(Random.Shared.NextDouble(), 2) + }); + + LastId++; + } + } + + base.OnInitialized(); + } + + class FlatModel + { + public int Id { get; set; } + public int? ParentId { get; set; } + public string Title { get; set; } + public double PercentComplete { get; set; } + public DateTime Start { get; set; } + public DateTime End { get; set; } + } +} +```` + +## See Also + +- [Gantt Overview - Telerik UI for Blazor](slug:gantt-overview) diff --git a/knowledge-base/grid-checkbox-column-prevent-click.md b/knowledge-base/grid-checkbox-column-prevent-click.md new file mode 100644 index 0000000000..a6fcdca7f8 --- /dev/null +++ b/knowledge-base/grid-checkbox-column-prevent-click.md @@ -0,0 +1,128 @@ +--- +title: Grid CheckBox column prevent accidental click +description: How to prevent an accidental click in the CheckBox column of the Grid +type: how-to +page_title: Grid CheckBox column prevent accidental click +slug: grid-kb-checkbox-column-prevent-click +position: +tags: grid, checkbox, prevent, click, deselection +res_type: kb +--- + +## Environment + + + + + + + + +
ProductGrid for Blazor
+ + +## Description + +An accidental click outside the checkbox in the Grid's CheckBox column deselects the currently selected rows. How to prevent that from happening and ensure that only a click on the checkbox deselects the respective row? + +## Solution + +You can prevent the click event of the cells in the CheckBox column of the Grid with JavaScript. + +1. Call a JavaScript function in the `OnAfterRenderAsync` method and in the `OnRead` event handler of the Grid. +2. In the function, attach a `click` event handler to the `td` Html elements in the CheckBox column and prevent the event conditionally + +```razor +@using Telerik.DataSource.Extensions; +@inject IJSRuntime JS; + + + + + + + + + +@code { + private List GridData { get; set; } + + private IEnumerable SelectedRows { get; set; } = Enumerable.Empty(); + + private string LastOnRead { get; set; } + + protected override void OnInitialized() + { + GenerateData(); + SelectedRows = new List() { GridData.ElementAt(2) }; + + base.OnInitialized(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender) + { + await Task.Delay(1); + await JS.InvokeVoidAsync("handleDeselection"); + } + await base.OnAfterRenderAsync(firstRender); + } + + private async Task OnGridRead(GridReadEventArgs args) + { + var result = GridData.ToDataSourceResult(args.Request); + args.Data = result.Data; + args.Total = result.Total; + + var now = DateTime.Now; + LastOnRead = now.ToLongTimeString() + "." + now.Millisecond; + + await Task.Delay(1); + await JS.InvokeVoidAsync("handleDeselection"); + } + + private void GenerateData() + { + GridData = new List(); + + for (int i = 1; i <= 100; i++) + { + GridData.Add(new SampleModel() { Id = i, Text = $"Item{i}" }); + } + } + + public class SampleModel + { + public int Id { get; set; } + public string Text { get; set; } + } +} +``` diff --git a/knowledge-base/grid-conditional-cell-background.md b/knowledge-base/grid-conditional-cell-background.md index e38b12d3f9..c04273531f 100644 --- a/knowledge-base/grid-conditional-cell-background.md +++ b/knowledge-base/grid-conditional-cell-background.md @@ -197,17 +197,17 @@ If you want to change the default row and alternating row backgrounds to match y background-color: red; } - .custom-row-colors .k-grid-table .k-table-row.k-master-row:hover { - background-color: pink; - } + .custom-row-colors .k-grid-table .k-table-row.k-master-row:hover { + background-color: pink; + } - .custom-row-colors .k-grid-table .k-master-row.k-alt { - background-color: green; - } + .custom-row-colors .k-grid-table .k-table-alt-row { + background-color: green; + } - .custom-row-colors .k-grid-table .k-master-row.k-alt:hover { - background-color: cyan; - } + .custom-row-colors .k-grid-table .k-table-row.k-table-alt-row:hover { + background-color: cyan; + } + + + Product + Grid for Blazor + + + + +## Description + +I want to display a popup beside an icon embedded within a cell in the [Grid for Blazor](slug:grid-overview). The popup should appear when the icon is clicked. + +## Solution + +To display a popup within a Grid cell: + +1. Add a button that triggers the popup for each row by using a `GridCommandColumn` or a column [`Template`](slug:grid-templates-column). + +2. Anchor the [`Popover`](slug:popover-overview) to the button via `AnchorSelector`. + +Here is an example implementation: + +````RAZOR + + + + @{ + var dataItem = (SampleModel)context; + } + + + + + + + + + + Popover for @ModelInPopup?.Name + + +
+ Hide +
+
+
+ +@code { + private List GridData { get; set; } = new(); + private TelerikPopover? PopoverRef { get; set; } + + private string PopoverAnchorSelector { get; set; } = "#button1"; + + private SampleModel? ModelInPopup { get; set; } + + private async Task OnPopupShowButtonClick(GridCommandEventArgs args) + { + ModelInPopup = (SampleModel)args.Item; + + PopoverRef?.Hide(); + PopoverAnchorSelector = $"#button{ModelInPopup.Id}"; + await Task.Delay(1); + + PopoverRef?.Show(); + } + + protected override void OnInitialized() + { + var rnd = new Random(); + for (int i = 1; i <= 7; i++) + { + GridData.Add(new SampleModel() + { + Id = i, + Name = $"Name {i}", + GroupName = $"Group {i % 3 + 1}", + Price = rnd.Next(1, 100) * 1.23m, + Quantity = rnd.Next(0, 1000), + StartDate = DateTime.Now.AddDays(-rnd.Next(60, 1000)), + IsActive = i % 4 > 0 + }); + } + } + public class SampleModel + { + public int Id { get; set; } + public int? ParentId { get; set; } + public string Name { get; set; } = string.Empty; + public string GroupName { get; set; } = string.Empty; + public decimal Price { get; set; } + public int Quantity { get; set; } + public object Icon { get; set; } = SvgIcon.File; + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public bool IsActive { get; set; } + } +} +```` + +## See Also + +* [Grid Overview Documentation](slug:grid-overview) +* [Popover Overview Documentation](slug:popover-overview) diff --git a/knowledge-base/grid-prevent-rowclick-when-selecting-text.md b/knowledge-base/grid-prevent-rowclick-when-selecting-text.md new file mode 100644 index 0000000000..25e76d3ccf --- /dev/null +++ b/knowledge-base/grid-prevent-rowclick-when-selecting-text.md @@ -0,0 +1,101 @@ +--- +title: Prevent RowClick Event When Selecting Text in Grid for Blazor +description: Learn how to prevent the RowClick event from being triggered in Grid for Blazor when selecting text using JavaScript interop. +type: how-to +page_title: Avoid RowClick Event Trigger During Text Selection in Grid for Blazor +meta_title: Avoid RowClick Event Trigger During Text Selection in Grid for Blazor +slug: grid-kb-prevent-rowclick-when-selecting-text +tags: grid, blazor, rowclick, text-selection +res_type: kb +ticketid: 1692651 +--- + +## Environment + + + + + + + +
Product Grid for Blazor
+ +## Description + +I want to prevent the [OnRowClick](slug:grid-events#onrowclick) event in the [Grid for Blazor](slug:grid-overview) from executing when a user selects text by dragging to highlight it. + +This knowledge base article also answers the following questions: +- How to prevent row click event in a Blazor Grid during text selection? +- How to check for text selection before firing Grid events? +- How to use JavaScript interop for handling the `OnRowClick` event in Telerik Blazor Grid? + +## Solution + +To achieve this, use JavaScript interop to detect text selection. Follow these steps: + +1. Define a JavaScript helper function that checks whether any text is currently selected on the page. +2. Inject the IJSRuntime service into your Blazor component to enable JavaScript interop. +3. Call the JavaScript function within your `OnRowClick` event handler to bypass logic when text is selected conditionally. + +````RAZOR +@inject IJSRuntime JS + +

Grid with Safe Row Click Handling

+ +@if (!string.IsNullOrEmpty(ClickedPersonName)) +{ +

Last clicked person: @ClickedPersonName

+} + + + + + + + + + +@code { + public class Person + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + } + + private readonly List People = new() + { + new Person { Id = 1, Name = "Alice Johnson", Email = "alice@example.com" }, + new Person { Id = 2, Name = "Bob Smith", Email = "bob@example.com" }, + new Person { Id = 3, Name = "Carol Lee", Email = "carol@example.com" } + }; + + private string ClickedPersonName = ""; + + private async Task OnRowClickHandler(GridRowClickEventArgs args) + { + var isTextSelected = await JS.InvokeAsync("isTextSelected"); + if (isTextSelected) + { + ClickedPersonName = "Text was selected, row click ignored."; + return; + } + + var item = (Person)args.Item; + ClickedPersonName = item.Name; + } +} + +@* Inline JavaScript for detecting text selection *@ + +```` + +## See Also + +* [Grid OnRowClick event](slug:grid-events#onrowclick) diff --git a/knowledge-base/grid-remove-group-indent.md b/knowledge-base/grid-remove-group-indent.md index e48cb8ea05..fb0bdb566b 100644 --- a/knowledge-base/grid-remove-group-indent.md +++ b/knowledge-base/grid-remove-group-indent.md @@ -48,7 +48,7 @@ To remove the group indent in the [Grid for Blazor](slug:grid-overview) and hide + +Chosen delivery method: @(ChosenDeliveryMethod == 0 ? "no selection yet" : ChosenDeliveryMethod.ToString()) +
+ + + + +@code { + private int ChosenDeliveryMethod { get; set; } + + private List DeliveryOptions { get; set; } = new List + { + new DeliveryMethodModel { MethodId = 1, MethodText = "Standard Shipping" }, + new DeliveryMethodModel { MethodId = 2, MethodText = "Express Shipping" }, + new DeliveryMethodModel { MethodId = 3, MethodText = "In-Store Pickup" }, + new DeliveryMethodModel { MethodId = 4, MethodText = "Curbside Pickup" }, + }; + + public class DeliveryMethodModel + { + public int MethodId { get; set; } + public string MethodText { get; set; } + } +} +```` + +## See Also + +* [RadioGroup Documentation](slug:radiogroup-overview) +* [CSS pointer-events Property](https://developer.mozilla.org/en-US/docs/Web/CSS/pointer-events) +* [RadioGroup Binding](slug:radiogroup-databind) diff --git a/knowledge-base/tabstrip-dynamic-tabs.md b/knowledge-base/tabstrip-dynamic-tabs.md index b2ae2f693b..d8b8895053 100644 --- a/knowledge-base/tabstrip-dynamic-tabs.md +++ b/knowledge-base/tabstrip-dynamic-tabs.md @@ -1,11 +1,11 @@ --- -title: Dynamic Tabs -description: How to create Dynamic Tabs in TabStip. +title: Add and Remove TabStrip Tabs +description: Learn how to dynamically add and remove tabs type: how-to -page_title: Dynamic Tabs -slug: tabstrip-kb-dynamic-tabs -position: -tags: +page_title: How to Add and Remove TabStrip Tabs +slug: tabstrip-kb-add-remove-tabs +tags: telerik,blazor,tabstrip,add tabs,remove tabs +ticketid: res_type: kb --- @@ -20,12 +20,180 @@ res_type: kb - ## Description -How to create Dynamic Tabs in TabStip? How to add and remove tabs dynamically? How to get information about the currently active tab? How to set the content of the tabs dynamically? +I have a collection of items representing separate tabs. I am iterating through that collection to render a tab for each item as shown in the [Tabs Collection article](slug:tabstrip-tabs-collection). I want to allow the user to add and remove tabs. How to achieve that? + +This KB article also answers the following questions: +* How to implement add and remove tab functionality with the Telerik TabStrip component. +* How to remove a tab using an "X" button in the tab header. +* How to add a new tab with a "+" button, similar to browser tab controls. +* How to position the add ("+") button next to the last tab header. ## Solution -An example is available in the following project: [https://github.com/telerik/blazor-ui/tree/master/tabstrip/DynamicTabs](https://github.com/telerik/blazor-ui/tree/master/tabstrip/DynamicTabs). \ No newline at end of file +1. [Render the TabStrip tabs in a loop](slug:tabstrip-tabs-collection). +1. Use a [`HeaderTemplate`](slug:tabstrip-header-template) for the tabs to add Remove buttons. You can display the buttons conditionally based on the tab count. +1. Declare a button for adding new tabs. +1. Use custom styling and JavaScript to position the Add button next to the last tab header. + +>caption Adding and removing TabStrip tabs at runtime + +````RAZOR +@inject IJSRuntime JS + +
+ + + + @foreach (Tab tab in Tabs) + { + + +
+ @tab.Title + @if (Tabs.Count > 1) + { + + } +
+
+ + Content for @tab.Title + +
+ } +
+
+ + + +@* Move JavaScript code to a JS file *@ + + + +@code { + private List Tabs = new List() + { + new Tab { Title = "Tab 1" }, + new Tab { Title = "Tab 2" }, + new Tab { Title = "Tab 3" } + }; + + private string ActiveTabId { get; set; } = string.Empty; + + private bool ShouldPositionAddButton { get; set; } + + private int LastTabNumber { get; set; } = 3; + + private void AddTab() + { + Tab tabToAdd = new Tab { Id = Guid.NewGuid().ToString(), Title = $"New Tab {++LastTabNumber}" }; + + Tabs.Add(tabToAdd); + + //In this example, we are always activating the newly added tab. Adjust the logic to activate a different tab if needed. + ActiveTabId = tabToAdd.Id; + + ShouldPositionAddButton = true; + } + + private void RemoveTab(Tab tab) + { + if (Tabs.Count <= 1) + { + return; + } + + // Activate the tab after or before the removed one if it's active + if (ActiveTabId == tab.Id) + { + int removedTabIndex = Tabs.FindIndex(x => x.Id == tab.Id); + if (removedTabIndex == Tabs.Count - 1) + { + ActiveTabId = Tabs.ElementAt(removedTabIndex - 1).Id; + } + else + { + ActiveTabId = Tabs.ElementAt(removedTabIndex + 1).Id; + } + } + + Tabs.Remove(tab); + + ShouldPositionAddButton = true; + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + if (firstRender || ShouldPositionAddButton) + { + ShouldPositionAddButton = false; + await JS.InvokeVoidAsync("positionAddTabButton"); + } + + await base.OnAfterRenderAsync(firstRender); + } + + public class Tab + { + public string Id { get; set; } = Guid.NewGuid().ToString(); + + public string Title { get; set; } = string.Empty; + } + +} +```` + +## See Also + +* [Dynamic Tab Collection](slug:tabstrip-tabs-collection) +* [TabStrip Tab `HeaderTemplate`](slug:tabstrip-header-template) diff --git a/knowledge-base/toolbar-vertical-orientation-display.md b/knowledge-base/toolbar-vertical-orientation-display.md new file mode 100644 index 0000000000..818e4c66d3 --- /dev/null +++ b/knowledge-base/toolbar-vertical-orientation-display.md @@ -0,0 +1,133 @@ +--- +title: Display Vertical ToolBar +description: Learn how to display the Telerik ToolBar for Blazor vertically. +type: how-to +page_title: How to Display Vertical ToolBar +slug: toolbar-kb-vertical-orientation-display +tags: telerik, blazor, toolbar +ticketid: 1693045 +res_type: kb +--- + +## Environment + + + + + + + + +
ProductToolBar for Blazor
+ +## Description + +This KB answers the following questions: + +* How to display the TelerikToolBar vertically? +* How to render the Telerik ToolBar for Blazor with vertical orientation? +* How to arrange the ToolBar buttons one below the other? + +## Solution + +1. Disable the automatic tool overflowing with `OverflowMode="@ToolBarOverflowMode.None"` or `Adaptive="false"` in older versions. +1. Set a custom CSS class to the ToolBar with the `Class` parameter. +1. Use the custom CSS class to [override the ToolBar CSS styles](slug:themes-override): + * Set `column` `flex-flow` for the ToolBar and any nested [ButtonGroups](slug:toolbar-built-in-tools#toolbarbuttongroup). See [Flexbox Guide](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) for more information. + * Reduce the component `width`. + * Adjust the `border-radius` values of buttons inside nested ButtonGroups. + +>caption Configure a vertical Telerik ToolBar for Blazor + +````RAZOR +

Block ToolBar

+ +before + + + + + + + + + + + + + + + +after + +

Inline ToolBar

+ +before + + + + + + + + + + + + + + + +after + + + +@code { + private bool BoldSelected { get; set; } + private bool ItalicSelected { get; set; } + private bool UnderlineSelected { get; set; } +} +```` + +## See Also + +* [ToolBar Overview](slug:toolbar-overview) diff --git a/knowledge-base/treelist-paging-pre-selected-node.md b/knowledge-base/treelist-paging-pre-selected-node.md new file mode 100644 index 0000000000..fa155129b6 --- /dev/null +++ b/knowledge-base/treelist-paging-pre-selected-node.md @@ -0,0 +1,120 @@ +--- +title: How to Initially Display the Page with a Pre-Selected Node in TreeList +description: Learn how to ensure a pre-selected node is visible by automatically navigating to its page in a Telerik TreeList with pagination enabled. +type: how-to +page_title: Navigate to Page Containing Pre-Selected Node in TreeList +meta_title: Navigate to Page Containing Pre-Selected Node in TreeList +slug: treelist-kb-paging-pre-selected-node +tags: treelist, paging, pre-selected, node, navigation, page, size, blazor +res_type: kb +ticketid: 1690423 +--- + +## Environment + + + + + + + + +
ProductTreeList for Blazor
+ +## Description + +I have a Telerik [TreeList](slug:treelist-overview) with a checkbox for selection and pagination enabled. I pre-select a node programmatically and need the TreeList to automatically navigate to the page containing the first selected node. + +## Solution + +To automatically show the page that contains the first pre-selected node in your Telerik TreeList with paging enabled, you need to programmatically set the TreeList's `Page` property based on the index of the selected node in your data. + +1. Identify the index of the first selected node in your data source. +2. Use the PageSize to determine on which page the node appears. +3. Assign the calculated page number to the TreeList's Page parameter before rendering. + +````Razor + + + + + + + + + +@code { + private List TreeListData { get; set; } = new(); + private IEnumerable SelectedEmployees { get; set; } = Enumerable.Empty(); + private int PageSize = 10; + private int CurrentPage = 1; + + protected override void OnInitialized() + { + TreeListData = new List(); + + for (int i = 1; i <= 59; i++) + { + TreeListData.Add(new Employee() + { + Id = i, + ParentId = i <= 3 ? null : i % 3 + 1, + FirstName = "First " + i, + LastName = "Last " + i, + Position = i <= 3 ? "Team Lead" : "Software Engineer" + }); + } + + SelectedEmployees = new List() { TreeListData.ElementAt(2) }; + + var selectedId = SelectedEmployees.FirstOrDefault()?.Id; + if (selectedId != null) + { + // Step 1: Flatten the tree as it would appear expanded + var flatList = new List(); + foreach (var root in TreeListData.Where(e => e.ParentId == null)) + { + FlattenHierarchy(root, flatList); + } + + // Step 2: Find index of selected item in flattened list + var index = flatList.FindIndex(e => e.Id == selectedId); + if (index >= 0) + { + CurrentPage = (index / PageSize) + 1; + } + } + } + + private void FlattenHierarchy(Employee node, List result) + { + result.Add(node); + var children = TreeListData.Where(e => e.ParentId == node.Id); + foreach (var child in children) + { + FlattenHierarchy(child, result); + } + } + + public class Employee + { + public int Id { get; set; } + public int? ParentId { get; set; } + public string FirstName { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; + public string Position { get; set; } = string.Empty; + } +} +```` + +## See Also + +* [TreeList Overview](slug:treelist-overview) +* [TreeList Paging](slug:treelist-paging) diff --git a/knowledge-base/value-changed-validation-model.md b/knowledge-base/value-changed-validation-model.md index e9f09b1928..7cc77e9a39 100644 --- a/knowledge-base/value-changed-validation-model.md +++ b/knowledge-base/value-changed-validation-model.md @@ -97,4 +97,4 @@ from model: @person.theTbValue ## See also -* [Knowledge Base article: Validate a Telerik component as child control and apply invalid border ](slug:inputs-kb-validate-child-component) +* [Validate a Telerik component as child control and apply invalid border ](slug:inputs-kb-validate-child-component) diff --git a/troubleshooting/license-key-errors.md b/troubleshooting/license-key-errors.md index e918d4aab1..bc6a20f1ef 100644 --- a/troubleshooting/license-key-errors.md +++ b/troubleshooting/license-key-errors.md @@ -22,6 +22,7 @@ A Telerik license key error may occur in the following scenarios: * The license key is outdated or does not include the product version that you are using. * Your subscription license or trial has expired. * You have different conflicting license keys in the same environment. For example, using one global license key and one in the app. Or, using a license key file together with an environment variable in CI/CD environment. +* Telerik UI for Blazor is used in the **Client** project of a WebAssembly app that uses pre-rendering. In such cases, you can briefly see a yellow banner in the browser, which says "[We couldn't verify your license key for Telerik UI for Blazor. Please see the build log for details and resolution steps](#we-couldn-t-verify-your-license-key-for-telerik-ui-for-blazor-yellow-banner)". Refer to the specific error messages and tips below. @@ -70,6 +71,12 @@ This error applies to subscription licenses. [Renew your subscription](https://w [Purchase a commercial license to continue using Telerik UI for Blazor](https://www.telerik.com/purchase/blazor-ui). +### We couldn't verify your license key for Telerik UI for Blazor (yellow banner) + +This section assumes an existing valid license key, so that the problem is not any of the above. + +If you see a yellow warning banner in the web browser that says "**We couldn't verify your license key**", then refer to [Using Telerik Packages in Referenced Projects](slug:installation-license-key#using-telerik-packages-in-referenced-projects). + ## See Also * [Download and Install License Key](slug:installation-license-key) diff --git a/upgrade/breaking-changes/9-0-0.md b/upgrade/breaking-changes/9-0-0.md index e0ddb02b16..5983ad4e40 100644 --- a/upgrade/breaking-changes/9-0-0.md +++ b/upgrade/breaking-changes/9-0-0.md @@ -8,9 +8,15 @@ position: 960 # Breaking Changes in 9.0.0 +## Common + ### Trial and commercial users now use the same product package The trial and commercial product versions merged into a single unified distribution package. The product access now depends on a license key file. This eliminates the need for separate trial downloads. For more information, please refer to the [Telerik License Key](slug:installation-license-key). ### .NET Support -Telerik UI for Blazor 9.0.0 targets .NET 8 and no longer supports .NET 6 and .NET 7. For more information, see [System Requirements](slug:system-requirements). \ No newline at end of file +Telerik UI for Blazor 9.0.0 targets .NET 8 and no longer supports .NET 6 and .NET 7. For more information, see [System Requirements](slug:system-requirements). + +## Window + +The `Centered` parameter is removed. The Window is centered by default if the `Top` and `Left` parameters are equal to an empty string or if they are not set. To center the Window programmatically at any time, [reset the `Top` and `Left` parameter values](slug:components/window/position#top-and-left). diff --git a/upgrade/breaking-changes/list.md b/upgrade/breaking-changes/list.md index 5c0891e0f4..15a88a8704 100644 --- a/upgrade/breaking-changes/list.md +++ b/upgrade/breaking-changes/list.md @@ -10,7 +10,7 @@ position: 0 This article lists the releases of the Telerik UI for Blazor product that introduce breaking changes. You may want to go through it when [upgrading](slug:upgrade-tutorial) to see whether you are affected. - +* [9.0.0](slug:changes-in-9-0-0) * [8.0.0](slug:changes-in-8-0-0) * [7.0.0](slug:changes-in-7-0-0) * [6.0.0](slug:changes-in-6-0-0) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy