MultiColumnComboBox Virtualization
The MultiColumnComboBox component can virtualize the elements in its dropdown so you can use huge data source without UI performance issues.
Enabling the UI virtualization feature makes the component reuse a set number of items in the dropdown as you scroll, instead of rendering out the entire data source. It can work both with local data that the view-model already has, or you can fetch remote data every time the user scrolls through an event the component provides.
In This Article
Basics
This section will explain the parameters and behaviors that are related to the virtualization feature so you can set it up.
-
ScrollMode
-Telerik.Blazor.DropDownScrollMode
- set it toDropDownScrollMode.Virtual
. It defaults to the "regular" scrolling. -
ListHeight
-string
- set the height of the dropdown. It must not be anull/empty
string. -
ItemHeight
-decimal
- set it to the height each individual item will have in the dropdown. Make sure to accommodate the content your items will have and any item template. -
PageSize
-int
- defines how many items will actually be rendered and reused. The value determines how many items are loaded on each scroll. The number of items must be large enough according to theItemHeight
and popupListHeight
, so that there are more items than the dropdown so there is a scrollbar.
You can find a basic example in the Local Data section below.
ValueMapper
-Func<TValue, Task<TItem>>
- the component will call this method to request the model that matches theValue
it has set. This is required because with remote data theValue
may not be in the initial collection of data that the component has, and so there would otherwise be no way to extract theDataTextField
from it to render it. Usually, this method will be called on the initial render only to fetch the data item for the current selection.OnRead
-EventCallback
- the component will call this event when the user scrolls with the corresponding offset (Skip
),PageSize
and any filters. This lets you optimize the data queries and return only what is needed at the moment, when it is needed. Set theargs.Data
andargs.Total
properties of the event argument object.
Limitations
- When the initially selected item/items are on a page different than the first one, opening the dropdown list will NOT scroll the list to the selected item.
- When virtualization is enabled, the component calculates the position of the items. In this case, the loading indicators are not displayed as they would affect the proper item positioning.
Local Data Example
@SelectedValue
<br />
<TelerikMultiColumnComboBox Data="@Data"
TItem="Person"
TValue="int"
TextField="@nameof(Person.Id)"
ValueField="@nameof(Person.Name)"
Filterable="false"
@bind-Value="@SelectedValue"
ItemHeight="30"
ListHeight="300px"
PageSize="10"
ScrollMode="@DropDownScrollMode.Virtual">
<MultiColumnComboBoxColumns>
<MultiColumnComboBoxColumn Field="@nameof(Person.Id)" Width="200px" />
<MultiColumnComboBoxColumn Field="@nameof(Person.Name)" Width="200px" />
</MultiColumnComboBoxColumns>
</TelerikMultiColumnComboBox>
@code {
int SelectedValue { get; set; }
List<Person> Data { get; set; }
protected override void OnInitialized()
{
Data = Enumerable.Range(1, 12345).Select(x => new Person { Id = x, Name = $"Name {x}" }).ToList();
base.OnInitialized();
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
}
Remote Data Example
This example showcases sample implementations of:
- An async remote service that returns the data. It is mocked by a static class for this example, you can refactor as needed, and you can find examples of serializing it over the wire in this collection of sample projects for the grid component - the approach is identical.
An
OnRead
event handler that calls that service.A
ValueMapper
that also calls the service.
Run this and see how you can display, scroll and filter over 10k records in the combobox without delays and performance issues from a remote endpoint. There is artificial delay in these operations for the sake of the demonstration.
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikMultiColumnComboBox TItem="Person"
TValue="int"
TextField="@TextField"
ValueField="@ValueField"
Filterable="false"
@bind-Value="@SelectedValue"
ItemHeight="@ItemHeight"
ListHeight="@ListHeight"
PageSize="@PageSize"
ScrollMode="@DropDownScrollMode.Virtual"
OnRead="@ReadItems"
ValueMapper="@ConvertValue">
<MultiColumnComboBoxColumns>
<MultiColumnComboBoxColumn Field="@nameof(Person.EmployeeId)" Width="200px" />
<MultiColumnComboBoxColumn Field="@nameof(Person.Name)" Width="200px" />
</MultiColumnComboBoxColumns>
</TelerikMultiColumnComboBox>
@code {
public string TextField { get; set; } = "Name";
public string ValueField { get; set; } = "EmployeeId";
public string ListHeight { get; set; } = "260px";
public int ItemHeight { get; set; } = 28;
public int PageSize { get; set; } = 10;
public int SelectedValue { get; set; } = 145;
public string SelectedValueCustom { get; set; } = "Employee 145";
public List<Person> AllData { get; set; }
protected Task<Person> ConvertValue(int selectedValue)
{
return Task.FromResult(AllData.FirstOrDefault(x => selectedValue == x.EmployeeId));
}
protected Task<Person> ConvertValueCustom(string selectedValue)
{
return Task.FromResult(AllData.FirstOrDefault(x => selectedValue == x.Name));
}
protected async Task ReadItems(MultiColumnComboBoxReadEventArgs args)
{
await LoadData();
var result = AllData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
}
// sample Read operation
private async Task LoadData()
{
if (AllData == null)
{
AllData = Enumerable.Range(0, 12345).Select(x => new Person
{
EmployeeId = x,
Name = $"Name {x}"
}).ToList();
}
}
public class Person
{
public int EmployeeId { get; set; }
public string Name { get; set; }
}
}