Skip to content
# BasicList ## Description BitBasicList renders a list of items into a scrolling region, one RowTemplate per item, and stays agnostic of the layout, of the tile the row is built from and of selection management. The items either come from a collection or from an ItemsProvider that is called for a region at a time. Virtualize keeps only the rows around the visible region in the DOM, so a set of any size costs the same to render, while LoadMore pages through the set instead, appending a page per click of its button or, with AutoLoad, per scroll to the end of the loaded ones. ## Parameters | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | AutoLoad | `bool` | false | Loads the next page as soon as the end of the loaded items scrolls into view, turning the LoadMore button into an infinite scrolling list. Only effective while LoadMore is enabled. | | AutoLoadThreshold | `int` | 0 | How many pixels before the end of the loaded items the next page starts loading in AutoLoad mode. | | Classes | `BitBasicListClassStyles?` | null | Custom CSS classes for different parts of the list. | | EmptyContent | `RenderFragment?` | null | The custom content that will be rendered when there is no item to show. | | FitHeight | `bool` | false | Sets the height of the list to fit its content. | | FitSize | `bool` | false | Sets the width and height of the list to fit its content. | | FitWidth | `bool` | false | Sets the width of the list to fit its content. | | FooterTemplate | `RenderFragment?` | null | The content rendered below the items of the list, inside its scrolling region. | | FullHeight | `bool` | false | Sets the height of the list to 100%. | | FullSize | `bool` | false | Sets the width and height of the list to 100%. | | FullWidth | `bool` | false | Sets the width of the list to 100%. | | HeaderTemplate | `RenderFragment?` | null | The content rendered above the items of the list, inside its scrolling region. | | Horizontal | `bool` | false | Lays the items of the list out in a row and scrolls it sideways instead of down. Ignored while Virtualize is enabled, since virtualization is vertical only. | | Items | `ICollection<TItem>?` | null | The list of items to render. | | ItemSize | `float` | 50 | Size of each item in pixels, used by the Virtualize mode to calculate the scroll range and the number of rows to render. | | ItemsProvider | `BitBasicListItemsProvider<TItem>?` | null | The function providing items to the list. It always takes priority over Items, and is called for a region at a time in Virtualize mode, for a page at a time in LoadMore mode, and once for the whole set otherwise. | | ItemsProviderDelay | `int` | 100 | The number of milliseconds the list waits before calling the ItemsProvider in Virtualize mode, which debounces the requests a scroll issues. A value of 0 turns the debouncing off. Never applies to the pages of the LoadMore mode. | | Loading | `bool` | false | Shows the loading content of the list in place of its items. The list also raises this state on its own while it is fetching items. | | LoadingTemplate | `RenderFragment?` | null | The template rendered while the list is loading its items. | | LoadMore | `bool` | false | Enables the LoadMore mode for the list. | | LoadMoreSize | `int` | 20 | The number of items to be loaded and rendered after the LoadMore button is clicked. | | LoadMoreTemplate | `RenderFragment<bool>?` | null | The template of the LoadMore button. Its context is whether a page is being loaded at that moment. | | LoadMoreText | `string?` | Load more | The custom text of the default LoadMore button. | | OnLoadingChange | `EventCallback<bool>` | | The callback that is invoked when the list starts and stops loading its items. | | OnLoadMore | `EventCallback<int>` | | The callback that is invoked after each page of the LoadMore mode has been appended, with the number of items the list holds at that point. | | OverscanCount | `int` | 3 | A value that determines how many additional items will be rendered before and after the visible region in Virtualize mode. | | Role | `string?` | list | The role attribute of the html element of the list. Set it to null to leave the role off altogether. | | RowTemplate | `RenderFragment<TItem>?` | null | The template to render each row. Without it each item is rendered as its own text. | | Styles | `BitBasicListClassStyles?` | null | Custom CSS styles for different parts of the list. | | Virtualize | `bool` | false | Enables virtualization in rendering the list. | | VirtualizePlaceholder | `RenderFragment<PlaceholderContext>?` | null | The template for items that have not yet rendered. | | AriaLabel | `string?` | null | Gets or sets the accessible label for the component, used by assistive technologies. | | Class | `string?` | null | Gets or sets the CSS class name(s) to apply to the rendered element. | | Dir | `BitDir?` | null | Gets or sets the text directionality for the component's content. | | ForceAnimation | `bool` | false | Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested. | | HtmlAttributes | `Dictionary<string, object>` | new Dictionary<string, object>() | Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. | | Id | `string?` | null | Gets or sets the unique identifier for the component's root element. | | IsEnabled | `bool` | true | Gets or sets a value indicating whether the component is enabled and can respond to user interaction. | | Style | `string?` | null | Gets or sets the CSS style string to apply to the rendered element. | | TabIndex | `string?` | null | Gets or sets the tab order index for the component when navigating with the keyboard. | | Visibility | `BitVisibility` | BitVisibility.Visible | Gets or sets the visibility state (visible, hidden, or collapsed) of the component. | ## Public Members | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | LoadMoreAsync | `Task` | | Loads the next page of the LoadMore mode, the same way clicking the LoadMore button does. | | RefreshDataAsync | `Task` | | Reloads the items of the list: a LoadMore list starts over from its first page, a virtualized provider list re-requests the region it shows, a plain provider list fetches the whole set again, and a plain list picks up the current contents of its Items collection. | | ScrollToEndAsync | `Task` | | Scrolls the list to its end. Pass true to animate the scrolling. | | ScrollToIndexAsync | `Task` | | Scrolls the list so that the item at the given index sits at its start edge. Pass true as the second argument to animate the scrolling. | | ScrollToOffsetAsync | `Task` | | Scrolls the list to an absolute offset in pixels on its scrolling axis. Pass true as the second argument to animate the scrolling. | | ScrollToStartAsync | `Task` | | Scrolls the list to its start. Pass true to animate the scrolling. | | UniqueId | `Guid` | Guid.NewGuid() | Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed. | | RootElement | `ElementReference` | | Gets the reference to the root HTML element associated with this component. | ## Enums ### BitVisibility Enum | Name | Value | Description | | :--- | :--- | :---------- | | Visible | 0 | The content of the component is visible. | | Hidden | 1 | The content of the component is hidden, but the space it takes on the page remains (visibility:hidden). | | Collapsed | 2 | The component is hidden (display:none). | ### BitDir Enum | Name | Value | Description | | :--- | :--- | :---------- | | Ltr | 0 | Ltr (left to right) is to be used for languages that are written from the left to the right (like English). | | Rtl | 1 | Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic). | | Auto | 2 | Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element. | ## Sub Classes ### BitBasicListClassStyles Properties | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Root | `string?` | null | Custom CSS classes/styles for the root element of the list. | | Header | `string?` | null | Custom CSS classes/styles for the header container of the list. | | Footer | `string?` | null | Custom CSS classes/styles for the footer container of the list. | | LoadingContent | `string?` | null | Custom CSS classes/styles for the loading container of the list. | | LoadMoreButton | `string?` | null | Custom CSS classes/styles for the LoadMore button of the list. | | LoadMoreText | `string?` | null | Custom CSS classes/styles for the LoadMore text of the list. | ## Examples \n**Basic**: ```razor
Name: @person.FirstName
``` ```csharp private readonly List fewPeople = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; private readonly List fruits = ["Apple", "Apricot", "Banana", "Cherry", "Fig", "Grape", "Lemon", "Mango", "Orange", "Peach"]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**Empty content**: ```razor
Name: @person.FirstName
Nobody to show here yet.
``` ```csharp private readonly List emptyPeople = []; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**Header & Footer**: ```razor People @fewPeople.Count
Name: @person.FirstName
- end of the list -
``` ```csharp private readonly List fewPeople = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**Loading**: ```razor
Name: @person.FirstName
Fetching people...
``` ```csharp private bool isLoading; private readonly List fewPeople = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**Virtualization**: ```razor

Id: @person.Id

Full Name: @person.FirstName @person.LastName

Job: @person.Job

``` ```csharp private readonly List lotsOfPeople = [.. Enumerable.Range(0, 8000).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**OverscanCount**: ```razor

Id: @person.Id

Full Name: @person.FirstName @person.LastName

Job: @person.Job

``` ```csharp private readonly List lotsOfPeople = [.. Enumerable.Range(0, 8000).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**ItemsProvider**: ```razor
Id: @product.Id
Name: @product.Name
Price: @product.Price
Id: Loading...
Name: Loading...
Price: Loading...
``` ```csharp [Inject] private HttpClient HttpClient { get; set; } = default!; [Inject] private NavigationManager NavManager { get; set; } = default!; private BitBasicListItemsProvider productsProvider; protected override void OnInitialized() { productsProvider = async req => { try { var query = new Dictionary() { { "$top", req.Count}, { "$skip", req.StartIndex } }; var url = NavManager.GetUriWithQueryParameters("api/Products/GetProducts", query); var data = await HttpClient.GetFromJsonAsync(url, AppJsonContext.Default.PagedResultProductDto); return BitBasicListItemsProviderResult.From(data!.Items, data!.TotalCount); } catch { return BitBasicListItemsProviderResult.Empty(); } }; base.OnInitialized(); } public class ProductDto { public int Id { get; set; } public string? Name { get; set; } public decimal Price { get; set; } public int CategoryId { get; set; } } [JsonSerializable(typeof(PagedResult))] public partial class AppJsonContext : JsonSerializerContext { } ``` \n**Grouped ItemsProvider**: ```razor @* Every row and the placeholder are 32px tall on purpose: the virtualization sizes its scroll region by a single item size, so rows of differing heights keep it correcting that size on every render and never let the list settle. *@ @if (catOrProd.IsProduct) {
Name: @catOrProd.Name
Price: @catOrProd.Price
} else {
@catOrProd.Name
}
Loading...
``` ```csharp [Inject] private HttpClient HttpClient { get; set; } = default!; [Inject] private NavigationManager NavManager { get; set; } = default!; private BitBasicListItemsProvider categoriesAndProductsProvider; protected override void OnInitialized() { categoriesAndProductsProvider = async req => { try { var query = new Dictionary() { { "$top", req.Count}, { "$skip", req.StartIndex } }; var url = NavManager.GetUriWithQueryParameters("api/Products/GetCategoriesAndProducts", query); var data = await HttpClient.GetFromJsonAsync(url, AppJsonContext.Default.PagedResultCategoryOrProductDto); return BitBasicListItemsProviderResult.From(data!.Items, data!.TotalCount); } catch { return BitBasicListItemsProviderResult.Empty(); } }; base.OnInitialized(); } public class CategoryOrProductDto { public int? ProductId { get; set; } public int? CategoryId { get; set; } public bool IsProduct => ProductId is not null; public string? Name { get; set; } public decimal? Price { get; set; } } [JsonSerializable(typeof(PagedResult))] public partial class AppJsonContext : JsonSerializerContext { } ``` \n**LoadMore**: ```razor
Name: @person.FirstName
Name: @person.FirstName
Name: @person.FirstName
Load more people
Full Name: @person.FirstName @person.LastName
Full Name: @person.FirstName @person.LastName
@if (isLoadingMore is false) { Load more people } else { Loading... }
Loaded so far: @loadedCount
Full Name: @person.FirstName @person.LastName
@if (isLoadingMore is false) { Load more people } else { Loading... }
``` ```csharp private int loadedCount; private readonly List fewPeople = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; private readonly List lotsOfPeople = [.. Enumerable.Range(0, 8000).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; private BitBasicListItemsProvider loadMoreProvider = default!; private BitBasicListItemsProvider loadMoreVirtualizeProvider = default!; protected override void OnInitialized() { loadMoreProvider = async req => { await Task.Delay(1000); return BitBasicListItemsProviderResult.From([.. fewPeople.Skip(req.StartIndex).Take(req.Count)], fewPeople.Count); }; loadMoreVirtualizeProvider = async req => { await Task.Delay(500); return BitBasicListItemsProviderResult.From([.. lotsOfPeople.Skip(req.StartIndex).Take(req.Count)], lotsOfPeople.Count); }; base.OnInitialized(); } public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**AutoLoad**: ```razor
Full Name: @person.FirstName @person.LastName
Loading more people...
``` ```csharp private readonly List lotsOfPeople = [.. Enumerable.Range(0, 8000).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; private BitBasicListItemsProvider autoLoadProvider = default!; protected override void OnInitialized() { autoLoadProvider = async req => { await Task.Delay(700); return BitBasicListItemsProviderResult.From([.. lotsOfPeople.Skip(req.StartIndex).Take(req.Count)], lotsOfPeople.Count); }; base.OnInitialized(); } public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**Horizontal**: ```razor
@person.FirstName
@person.Job
``` ```csharp private readonly List fewPeople = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**Scrolling & refreshing**: ```razor Scroll to start Scroll to end Scroll to #51 Add a person
@person.Id - @person.FirstName
``` ```csharp private bool scrollToEndPending; private BitBasicList? listRef; private readonly List mutablePeople = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; private async Task AddPerson() { var id = mutablePeople.Count + 1; mutablePeople.Add(new Person { Id = id, FirstName = $"Person {id}", LastName = $"Person Family {id}", Job = $"Programmer {id}" }); // The collection instance itself did not change, so the list is told to pick up its new contents. if (listRef is not null) { await listRef.RefreshDataAsync(); // The new row is only scrollable to once it has been rendered, so the scrolling waits for that render. scrollToEndPending = true; } } protected override async Task OnAfterRenderAsync(bool firstRender) { if (scrollToEndPending && listRef is not null) { scrollToEndPending = false; await listRef.ScrollToEndAsync(true); } await base.OnAfterRenderAsync(firstRender); } public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**Style & Class**: ```razor
Id: @person.Id Full Name: @person.FirstName Job: @person.Job
People
Name: @person.FirstName
``` ```csharp private readonly List lotsOfPeople = [.. Enumerable.Range(0, 8000).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; private readonly List fewPeople = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"Person {i + 1}", LastName = $"Person Family {i + 1}", Job = $"Programmer {i + 1}" })]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ``` \n**RTL**: ```razor

شناسه: @person.Id

نام کامل: @person.FirstName @person.LastName

شغل: @person.Job

``` ```csharp private readonly List fewPeopleRtl = [.. Enumerable.Range(0, 100).Select(i => new Person { Id = i + 1, FirstName = $"شخص {i + 1}", LastName = $"نام خانواگی شخص {i + 1}", Job = $"برنامه نویس {i + 1}" })]; public class Person { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Job { get; set; } } ```