DxTagBox<TData, TValue> Class
An editor that allows users to select multiple items (tags) from the drop-down list.
Namespace: DevExpress.Blazor
Assembly: DevExpress.Blazor.v24.1.dll
NuGet Package: DevExpress.Blazor
Declaration
public class DxTagBox<TData, TValue> :
DxDropDownListEditorBase<TData, TValue>,
ITagBox<TData, TValue>,
IDropDownListEditorBase<TData, TValue>,
IListEditorBase<TData, TValue>,
IDropDownOwner,
IFocusableEditor
Type Parameters
Name | Description |
---|---|
TData | The data item type. |
TValue | The value type. |
Remarks
The DevExpress TagBox for Blazor (<DxTagBox>
) component displays a drop-down window with a list of strings. Users can select multiple items from a list and type text in the editor to filter list items that contain the search string. Users can also use keyboard to navigate to the editor’s items and select them.
Add a TagBox to a Project
Follow the steps below to add the TagBox component to an application:
- Use a DevExpress Project Template to create a new Blazor Server or Blazor WebAssembly application. If you use a Microsoft project template or already have a Blazor project, configure your project to incorporate DevExpress Blazor components.
- Add the
<DxTagBox>
…</DxTagBox>
markup to a.razor
file. - Bind the component to data.
- Configure the component: customize the layout and appearance of items and tags, add a clear button and placeholder, and so on (see the sections below).
.NET 8 and .NET 9 Specifics
Blazor TagBox does not support static render mode. Enable interactivity to use the component in your application. Refer to the following topic for more details: Enable Interactive Render Mode.
Bind to Data
Strongly Typed Collection
Use the Data property to bind the TagBox to a strongly typed collection. Initialize this collection in the OnInitialized lifecycle method or before this method is invoked. Use the DataAsync property instead of the Data
property if a strongly typed collection is loaded asynchronously (for instance, from an HTTP request).
Use the Values property to specify the component’s selected value/item collection. You can use the @bind attribute to bind the Values
property to a data field. Refer to the following topic for details: Two-Way Data Binding.
<DxTagBox Data="@Cities" @bind-Values="@Values"></DxTagBox>
@code {
IEnumerable<string> Cities = new List<string>() {
"London",
"Berlin",
"Paris"
};
IEnumerable<string> Values { get; set; }
}
The TagBox can detect IQueryable collections and use benefits of the corresponding LINQ query providers (such as Entity Framework).
Custom Object Collection
You can bind the TagBox to a data collection (Data
) that stores custom objects (IEnumerable<CustomType>
). The Values
collection can also contain custom objects. In this case, you need to override the following methods for your custom object:
GetHashCode(). The TagBox uses hash codes to compare items. Override this method to make sure that compared objects have the same hash code.
Equals. The default
Equals
method determines whether two object instances are equal. The compared items will not be equal if they are different instances (even if they have exactly the same properties). As a result, the component will not display the selected items.
You also need to set the TextFieldName property. It specifies the custom object’s field name that returns strings to be shown in the TagBox. If the TextFieldName
property is not specified, the TagBox component searches for a Text field in the data source and uses this field as a text field. Otherwise, the TagBox is populated with CustomType.ToString() values.
As an alternative, you can use KeyFieldName and KeyFieldNames properties to specify which field the TagBox should use as an identifier to compare items.
@using BlazorApp.Data
<DxTagBox Data="@Staff.DataSource"
@bind-Values="@SelectedStaff"
TextFieldName="@nameof(Person.Text)">
</DxTagBox>
@code {
IEnumerable<Person> SelectedStaff { get; set; } = new List<Person>() { Staff.DataSource[0] };
}
If the TextFieldName
property is not specified, the TagBox’s items are populated with CustomType.ToString()
values.
Load Custom Data
When you bind an editor to data from a Web API service, assign the data type to the component’s T
parameter and use the CustomData property to implement custom data load logic.
You can also enable Virtual Scrolling to improve client-side performance.
Tags, Custom Tags, and List Items
The TagBox’s drop-down window displays a list of items from a bound data source. Once a user selects an item, its text is displayed as a new tag and the item is hidden from the list. To display the selected items in the list, set the HideSelectedItems property to false
.
Set the AllowCustomTags property to true
to allow users to type tags that are not in the list.
<DxTagBox Data="@Cities"
AllowCustomTags="true"
@bind-Tags="@Tags"
@bind-Values="@Values"
HideSelectedItems="false">
</DxTagBox>
@code {
IEnumerable<string> Tags = new List<string>() {
"London",
"New York"
};
IEnumerable<string> Cities = new List<string>() {
"London",
"Berlin",
"Paris",
};
IEnumerable<string> Values;
}
Use the Tags property to specify tags in code. You can use the @bind attribute to bind the Tags
property to a data field. Refer to the following topic for details: Two-Way Data Binding. If you do not use two-way binding, handle the TagsChanged event to respond to the tag collection’s changes.
Multiple Columns
The TagBox control can display data across multiple columns. Follow the steps below to create columns:
- Add a
<Columns>
tag to the component’s markup to declare the column collection. Populate the
Columns
collection with DxListEditorColumn objects. These objects include the following properties:- Caption - Specifies the column’s caption text.
- FieldName - Specifies the data source field that populates column items in multi-column editors.
- SearchEnabled - Specifies whether the component can search text in cells of the current column.
- Visible - Specifies whether the column is visible.
- VisibleChanged - Fires when the column visibility changes.
- VisibleIndex - Specifies a column’s position among other columns.
- VisibleIndexChanged - Fires when the column’s visible index changes.
- Width - Specifies the column’s width.
To format tag values, use the EditFormat property.
@using StaffData
<DxTagBox Data="@Staff.DataSource"
@bind-Values="@SelectedStaff"
EditFormat="{1} {2}">
<Columns>
<DxListEditorColumn FieldName="Id" Width="50px" />
<DxListEditorColumn FieldName="FirstName" Caption="Name"/>
<DxListEditorColumn FieldName="LastName" Caption="Surname"/>
</Columns>
</DxTagBox>
@code {
IEnumerable<Person> SelectedStaff { get; set; } = new List<Person>() { Staff.DataSource[0] };
}
Clear Button and Placeholder
Set the ClearButtonDisplayMode property to Auto
to show the Clear button when the TagBox has tags. Users can click this button to clear all the displayed tags (set the Tags property to null
).
Use the NullText property to display the prompt text (placeholder) in TagBox when its Tags property is set to null
.
<DxTagBox NullText="Select countries..."
Data="@CountryData.Countries"
@bind-Values="@SelectedCountries"
TextFieldName="@nameof(Country.CountryName)"
ClearButtonDisplayMode="DataEditorClearButtonDisplayMode.Auto">
</DxTagBox>
@code {
IEnumerable<Country> SelectedCountries { get; set; }
}
Search and Filter Data
The DevExpress Blazor TagBox can search for text, and filter and highlight search results. Use the following API members to enable search and filter capabilities:
SearchMode — Specifies whether the component can search for text that users type in the edit box. When the AutoSearch mode is active, the TagBox filters its items based on the search string and highlights string matches. The search operation includes all visible columns.
Users can use special characters to create composite criteria. Refer to the following section for additional information: Search Syntax.
- SearchFilterCondition — Specifies the search and filter condition (Contains, Equals, or StartsWith).
- SearchTextParseMode — Specifies how the component combines words into the search query. If search text contains multiple words separated by spaces, words can be treated as single or individual conditions. The GroupWordsByAnd, GroupWordsByOr, and ExactMatch modes are available.
- SearchEnabled - Specifies whether the component can search text in cells of the current column.
<DxTagBox Data="Staff.DataSource"
@bind-Values="@Values"
TextFieldName="@nameof(Person.FirstName)"
ValueFieldName="@nameof(Person.Id)"
SearchMode="ListSearchMode.AutoSearch"
SearchFilterCondition="ListSearchFilterCondition.Contains">
<Columns>
<DxListEditorColumn FieldName="FirstName"></DxListEditorColumn>
<DxListEditorColumn FieldName="LastName"></DxListEditorColumn>
<DxListEditorColumn FieldName="Department" SearchEnabled="false"></DxListEditorColumn>
</Columns>
</DxTagBox>
@code{
IEnumerable<int> Values { get; set; }
}
When a user types text into the edit box, the TagBox filters and highlights search results.
Note
If you use the EditBoxDisplayTemplate property and need to enable filter mode, you should add a DxInputBox object to the template markup.
Customization
The TagBox component allows you to customize its appearance: the size, the appearance of the edit box, items, tags, and column cells.
Size Modes
Use the SizeMode property to specify a TagBox size. The following code snippet applies different size modes to TagBox components.
@using StaffData
<DxTagBox Data="@Staff.DataSource"
TextFieldName="@nameof(Person.Text)"
@bind-Values="@SelectedStaff"
SizeMode="SizeMode.Small"></DxTagBox>
<DxTagBox Data="@Staff.DataSource"
TextFieldName="@nameof(Person.Text)"
@bind-Values="@SelectedStaff"
SizeMode="SizeMode.Medium"></DxTagBox>
<DxTagBox Data="@Staff.DataSource"
TextFieldName="@nameof(Person.Text)"
@bind-Values="@SelectedStaff"
SizeMode="SizeMode.Large"></DxTagBox>
@code {
IEnumerable<Person> SelectedStaff = new List<Person>() { Staff.DataSource[0] };
}
For more information, refer to the following topic: Size Modes.
Item Display Template
Use the ItemDisplayTemplate property to customize the appearance of TagBox items. The property accepts a TagBoxItemDisplayTemplateContext<TData> object as the context parameter. You can use the parameter’s members to obtain item information:
The following code snippet uses the ItemDisplayTemplate
property to display TagBox items in a card-like view. Each item shows an employee’s first name, last name, photo, and phone number.
@inject NwindDataService NwindDataService
<DxTagBox Data="@Data"
@bind-Values="@Values">
<TagDisplayTemplate Context="tagContext">
<div class="tagbox-tag-template">
<img class="tagbox-tag-template-employee-photo" src="@StaticAssetUtils.GetImagePath(GetImageFileName(tagContext.DataItem))" alt="@tagContext.DataItem.FullName" />
<div>@tagContext.DataItem.FullName</div>
<DxButton Click="@tagContext.RemoveTagAction"
@onclick:stopPropagation
aria-label="Remove Tag"
CssClass="tagbox-tag-template-close-btn"
IconCssClass="tagbox-tag-template-close-btn-icon"
RenderStyle="ButtonRenderStyle.None" RenderStyleMode="ButtonRenderStyleMode.Text">
</DxButton>
</div>
</TagDisplayTemplate>
<ItemDisplayTemplate>
<div class="tagbox-item-template">
<img class="tagbox-item-template-employee-photo" src="@StaticAssetUtils.GetImagePath(GetImageFileName(context.DataItem))" alt="@context.DataItem.FullName" />
<div>
<span class="tagbox-item-template-employee-first-name">@context.DataItem.FullName</span>
<span class="tagbox-item-template-employee-home-phone">@context.DataItem.HomePhone</span>
</div>
</div>
</ItemDisplayTemplate>
</DxTagBox>
@code {
IEnumerable<Employee> Data { get; set; }
IEnumerable<Employee> Values { get; set; }
protected override async Task OnInitializedAsync() {
Data = await NwindDataService.GetEmployeesAsync();
Values = Data.Take(1);
}
string GetImageFileName(Employee employee) {
return $"employees/item-template{employee.EmployeeId}.jpg";
}
}
Tag Display Template
Use the TagDisplayTemplate property to customize tag appearance. The property accepts a TagBoxTagDisplayTemplateContext<TData> object as the context parameter. You can use the parameter’s members to obtain tag information:
The following code snippet customizes a tag appearance according to its type and text.
@inject WorldcitiesDataService WorldcitiesDataService
<DxTagBox Data="@Data"
TextFieldName="@nameof(City.CityName)"
TData="City"
TValue="City"
AllowCustomTags="true"
@bind-Tags="@Tags"
ListRenderMode="ListRenderMode.Virtual">
<TagDisplayTemplate Context="tagContext">
@{
var buttonStyleMode = tagContext.IsCustom ? ButtonRenderStyleMode.Contained : GetModeByID(tagContext.DataItem.CityName);
var buttonStyle = tagContext.IsCustom ? ButtonRenderStyle.Dark : ButtonRenderStyle.Primary;
<DxButton RenderStyle="@buttonStyle"
RenderStyleMode="@buttonStyleMode"
Text="@tagContext.DisplayText"
CssStyle="display:inline-block; padding-right: 0">
@context
<span @onclick="@tagContext.RemoveTagAction" style="display:inline-block; width: 1em; margin-left: 0.5em">
×
</span>
</DxButton>
}
</TagDisplayTemplate>
</DxTagBox>
@code {
IEnumerable<City> Data { get; set; }
IEnumerable<string> Tags { get; set; }
protected override async Task OnInitializedAsync() {
Data = await WorldcitiesDataService.GetCitiesAsync();
Tags = new List<string>() { "New York", "Los Angeles", "Tokyo" };
}
ButtonRenderStyleMode GetModeByID(string cityName) {
switch(cityName) {
case "New York":
return ButtonRenderStyleMode.Contained;
case "Los Angeles":
return ButtonRenderStyleMode.Outline;
default:
return ButtonRenderStyleMode.Text;
}
}
}
Column Cell Display Template
The ColumnCellDisplayTemplate allows you to specify custom content and change cell appearance in TagBox columns. The template accepts a TagBoxColumnCellDisplayTemplateContext<TData> object as the context
parameter. You can use the parameter’s members to obtain column information.
The following code customizes the appearance of different columns.
@inject NwindDataService NwindDataService
<DxTagBox TData="Product" TValue="Product" Data="@Products"
@bind-Values="@SelectedProducts"
TextFieldName="@nameof(Product.ProductName)"
ListRenderMode="ListRenderMode.Virtual">
<Columns>
<DxListEditorColumn FieldName="@nameof(Product.ProductName)" Caption="Product" Width="30%" />
<DxListEditorColumn FieldName="@nameof(Product.UnitPrice)" Caption="Unit Price" />
<DxListEditorColumn FieldName="@nameof(Product.Quantity)" />
<DxListEditorColumn FieldName="@nameof(Product.Discount)" />
<DxListEditorColumn FieldName="@nameof(Product.Total)" />
</Columns>
<ColumnCellDisplayTemplate>
@GetColumnCellTemplateRenderFragment(context)
</ColumnCellDisplayTemplate>
</DxTagBox>
@code {
const int SelectedProductsCount = 3;
const int TotalPriceCeiling = 100;
IEnumerable<Product> Products { get; set; }
IEnumerable<Product> SelectedProducts { get; set; }
const string AlignRightCssClass = "align-right";
const string TextGreenCssClass = "text-green";
const string TextRedCssClass = "text-red";
protected override async Task OnInitializedAsync() {
var invoices = await NwindDataService.GetInvoicesAsync();
var products = await NwindDataService.GetProductsAsync();
Products = invoices.Join(products, i => i.ProductId, p => p.ProductId, (i, p) =>
new Product {
ProductName = i.ProductName,
UnitPrice = i.UnitPrice,
Quantity = i.Quantity,
Discount = i.Discount
}
);
SelectedProducts = Products.Skip(1).Take(SelectedProductsCount);
}
RenderFragment GetColumnCellTemplateRenderFragment(TagBoxColumnCellDisplayTemplateContext<Product> context) {
object displayValue;
string cssClass = string.Empty;
switch(context.Column.FieldName) {
case nameof(Product.UnitPrice):
cssClass = AlignRightCssClass;
displayValue = $"{context.Value:c}";
break;
case nameof(Product.Quantity):
cssClass = AlignRightCssClass;
displayValue = context.DisplayText;
break;
case nameof(Product.Discount):
displayValue = $"{context.Value:p}";
break;
case nameof(Product.Total):
var value = context.DataItem.UnitPrice * context.DataItem.Quantity;
var valueCssClass = value >= TotalPriceCeiling ? TextGreenCssClass : TextRedCssClass;
cssClass = $"{AlignRightCssClass} {valueCssClass}";
displayValue = $"{value:c}";
break;
default:
displayValue = context.DisplayText;
break;
}
return @<text><div class="@cssClass">@displayValue</div></text>;
}
public class Product {
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public float Discount { get; set; }
public int Total { get; set; }
public override bool Equals(object obj) {
return obj is Product product && string.Equals(product.ProductName, ProductName) && product.Quantity == Quantity;
}
public override int GetHashCode() {
return HashCode.Combine(ProductName, Quantity);
}
}
}
Empty Data Area Template
The TagBox displays an empty data area in the following cases:
- The Data property is unset.
- The specified data source is empty.
- None of the TagBox items matches the current search and filter condition.
- You use the DataAsync property or the CustomData property to bind the TagBox to a data source. The component sends the first request to a remote data source and waits for a response.
Use the EmptyDataAreaTemplate to customize content displayed in this empty region. The template’s context
parameter includes the IsDataLoading property that allows you to determine whether the TagBox is still loading data.
@inject NwindDataService NwindDataService
<DxTagBox Data="@DataItems"
@bind-Values="@Items"
CssClass="cw-480"
NullText="Select an order..."
>
<EmptyDataAreaTemplate>
@if (!context.IsDataLoading) {
<div class="empty-data-area-template">
<div class="d-flex flex-column">
No orders found
</div>
</div>
}
</EmptyDataAreaTemplate>
</DxTagBox>
@code {
IEnumerable<string> DataItems { get; set; }
IEnumerable<string> Items { get; set; }
}
Virtual Scrolling
When virtual scrolling is activated (ListRenderMode is set to Virtual
), the TagBox renders data on demand when a user scrolls its items.
<DxTagBox Data="@Strings"
@bind-Values="@Values"
ListRenderMode="ListRenderMode.Virtual">
</DxTagBox>
Input Validation
When you add the TagBox to the Blazor’s standard EditForm, you can use the ValidateBy property to validate component tags and values.
The following code snippet uses the ValidateBy
property to validate email addresses (custom tags) specified in the TagBox. In this example the following validation settings are specified:
- the
ValidateBy
property is set to TagBoxValidateBy.Tags; - the Tags property’s bound field is marked with the Required and custom data annotation attributes that validate email formats.
After a user types an email address, the edit box is underlined in red or green: red indicates the editor contains an invalid tag(s) or is empty; green indicates the tags are valid.
<EditForm Model="@recipients" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
<DataAnnotationsValidator />
<p>
<label for="emails">Recipients:</label>
<DxTagBox Id="emails"
NullText="Select email recipients"
Data="@Emails.DataSource"
TData="string"
TValue="string"
AllowCustomTags="true"
ValidateBy="TagBoxValidateBy.Tags"
ShowValidationIcon="true"
@bind-Tags="@recipients.Data">
</DxTagBox>
<ValidationMessage For="@(() => recipients.Data)" />
</p>
<button type="submit">Submit</button>
</EditForm>
@code {
EmailRecipients recipients = new EmailRecipients();
private void HandleValidSubmit() {
Console.WriteLine("OnValidSubmit");
}
private void HandleInvalidSubmit() {
Console.WriteLine("OnInvalidSubmit");
}
}
For more information, refer to the following help topic: Validate Input.
Keyboard Navigation
The DevExpress Blazor TagBox supports keyboard navigation. Users can focus the editor’s input element, navigate within the drop-down item list, and select tags. While DOM focus refers to the input element as the active element for keyboard interaction, visual focus can move to items in the drop-down list. Keyboard navigation is also supported in virtual scroll mode.
Shortcut Keys for Input Element
The following shortcut keys are available when the editor’s input element is focused:
Shortcut Keys | Description |
---|---|
Tab | Moves focus to the next focusable element on a page. Note that tags in the input and the Clear button are excluded from the page tab sequence. If AllowCustomTags is set to true , the entered characters are applied as a custom tag. |
Shift + Tab | Moves focus to the previous focusable element on a page. |
Characters | If AllowCustomTags is set to true , adds characters to the input. |
Backspace, Delete | If AllowCustomTags is set to true , removes characters in the input element. If there are no characters in the input, Backspace removes the last existing tag. |
Home | If AllowCustomTags is set to true , moves the cursor to the start of the text input. |
End | If AllowCustomTags is set to true , moves focus to the end of the text input. |
Right Arrow | If AllowCustomTags is set to true , moves the cursor one character to the right in the input element. When the cursor reaches a right edge, nothing happens. |
Left Arrow | If AllowCustomTags is set to true , moves the cursor one character to the left in the input element. When the cursor reaches a left edge, nothing happens. |
Alt + Down Arrow | Opens the drop-down list, but keeps focus in the input element. |
Shortcut Keys for Drop-Down Item List
The following shortcut keys are available when the drop-down list is opened:
Shortcut Keys | Description |
---|---|
Up Arrow | Moves focus one item up. |
Down Arrow | Moves focus one item down. |
Ctrl + Shift + Home | Moves focus to the first item in the list. |
Ctrl + Shift + End | Moves focus to the last item in the list. |
Page Up | Moves focus to the previous item page. |
Page Down | Moves focus to the next item page. |
Tab | Selects the focused item, updates the Tags parameter value, closes the drop-down list, moves focus to the input element. |
Enter | Selects the focused item, updates the Tags parameter value, and closes the drop-down list. |
Escape, Alt + Up Arrow | Closes the drop-down list. |
The Tag Box does not currently allow you to navigate through elements in templated items.
Read-Only State
The TagBox supports a read-only state. Set the ReadOnly property to true
to activate this mode.
<DxTagBox Data="@Fruits"
@bind-Values="@SelectedFruits"
ReadOnly="true">
</DxTagBox>
Drop-Down List Width
Use the DropDownWidthMode property to specify the width of the drop-down list. The following values are available:
ContentOrEditorWidth
(Default) - The list displays item text completely. The minimum list width matches the editor.ContentWidth
- The list width is equal to the width of the longest list item.EditorWidth
- The list width matches the editor. If item text does not fit the editor width, item text is displayed across multiple lines.
Drop-Down Window Direction
Use the DropDownDirection property to specify the direction in which the TagBox’s drop-down window is displayed relative to the input element. The default value is Down
. The following code changes the direction to Up
:
<DxTagBox Data="@Cities" @bind-Values="@Values" DropDownDirection="DropDownDirection.Up" />
@code {
IEnumerable<string> Cities = new List<string>() {
"London",
"Berlin",
"Paris",
};
IEnumerable<string> Values { get; set; }
}
Note
If the editor is close to a browser window’s edge and there is not enough space to display the drop-down window in the specified direction, the drop-down window is displayed in the opposite direction.
HTML Attributes and Events
You can use HTML attributes and events to configure the TagBox.
<DxTagBox Data="@Strings"
@bind-Values="@Values"
maxlength="10"
@onclick="MyFunction">
</DxTagBox>
@code {
void MyFunction(){
//...
}
}
Note that the TagBox’s CssClass property has priority over unmatched attributes (the Attributes property). For example, if you specify the class
html attribute and the CssClass
property, the last one has priority.
<DxTagBox Data="_data"
@bind-Values="_values"
class="myClass"
CssClass="myCssClass" />
Troubleshooting
If a Blazor application throws unexpected exceptions, refer to the following help topic: Troubleshooting.