11 KiB
title | description | type | page_title | slug | position | tags | ticketid | res_type |
---|---|---|---|---|---|---|---|---|
Filter or Search TreeView Items | How to filter TreeView items by different filter operators. How to search in TreeView items. | how-to | How to Filter or Search TreeView Items | treeview-kb-filter-items | telerik, blazor, treeview, filter, search | 1629723, 1468684, 1547890, 1578053, 1541792 | kb |
Environment
Product | TreeView for Blazor |
Description
This KB article answers the following questions:
- How to build manual filter function for the Telerik Blazor TreeView?
- How to filter TreeView items?
- How to allow users to search in the TreeView items?
- How to put search box in the Tree View to filter tree records?
- How to search the TreeView nodes, for example, folders and files?
- How to highlight the search text in the found TreeView items?
Solution
There are different ways to implement TreeView filtering, depending on the used [TreeView data binding]({%slug components/treeview/data-binding/overview%}).
The suggested approach below relies on [flat TreeView data]({%slug components/treeview/data-binding/flat-data%}). See this TreeView demo for a hierarchy data filtering example.
In both scenarios, there should be no [loading of TreeView items on demand]({%slug components/treeview/data-binding/load-on-demand%}), otherwise the filtering will not provide all possible results.
- Add a [TextBox]({%slug components/textbox/overview%}) for the filter string.
- (optional) Add a [DropDownList]({%slug components/dropdownlist/overview%}) or another suitable component for the filter operator.
- Use a [Button
OnClick
event]({%slug button-events%}) or the [TextBoxValueChanged
event]({%slug components/textbox/events%}) to trigger the TreeView item search process. - Create a
DataSourceRequest
object and populate itsFilters
property with a singleFilterDescriptor
.- If you need more complex filtering logic, use one or more
CompositeFilterDescriptor
s.
- If you need more complex filtering logic, use one or more
- Execute the [
ToDataSourceResult()
extension method]({%slug common-features-data-binding-onread%}#todatasourceresult-method) on the TreeViewData
. You will need to import theTelerik.DataSource.Extensions
namespace. - (optional) Add any missing parent items to the filtered items collection.
- (optional) Use a [TreeView
ItemTemplate
]({%slug components/treeview/templates%}) to highlight the search string inside the displayed TreeView items.
tip If the filtering operator is fixed (for example,
Contains
), you can replace steps 4 and 5 with a standard LINQ expression:
var filteredItems = FlatData.Where(x => x.Text.Contains(TreeViewFilterValue)).ToList();
caption Searching TreeView items with different filter operators
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikTextBox Value="@TreeViewFilterValue"
ValueChanged="@TreeViewFilterValueChanged"
DebounceDelay="500"
Placeholder="Type number or letter"
Width="180px" />
<TelerikDropDownList Data="@StringFilterOperators"
TItem="@FilterOperatorItem"
TValue="@FilterOperator"
TextField="@nameof(FilterOperatorItem.Text)"
ValueField="@nameof(FilterOperatorItem.Operator)"
Value="@TreeViewFilterOperator"
ValueChanged="@TreeViewFilterOperatorChanged"
Width="180px" />
<p>@FilterLog</p>
<TelerikTreeView Data="@FilteredData" @bind-ExpandedItems="@ExpandedItems">
<TreeViewBindings>
<TreeViewBinding>
<ItemTemplate>
@{
var item = (TreeItem)context;
if (string.IsNullOrEmpty(TreeViewFilterValue) ||
TreeViewFilterOperator == FilterOperator.DoesNotContain)
{
@item.Text
}
else if (TreeViewFilterOperator == FilterOperator.IsContainedIn &&
TreeViewFilterValue.Contains(item.Text))
{
<strong>@item.Text</strong>
}
else
{
@( new MarkupString(item.Text.Replace(
TreeViewFilterValue,
$"<strong>{TreeViewFilterValue.ToUpper()}</strong>",
StringComparison.InvariantCultureIgnoreCase)
) )
}
}
</ItemTemplate>
</TreeViewBinding>
</TreeViewBindings>
</TelerikTreeView>
<style>
.k-treeview strong {
color: red;
background: yellow;
}
</style>
@code {
private List<TreeItem> FlatData { get; set; } = new List<TreeItem>();
private List<TreeItem> FilteredData { get; set; } = new List<TreeItem>();
private IEnumerable<object> ExpandedItems { get; set; } = new List<TreeItem>();
private string FilterLog { get; set; } = string.Empty;
#region Filtering Logic
private string TreeViewFilterValue { get; set; } = string.Empty;
private FilterOperator TreeViewFilterOperator { get; set; } = FilterOperator.Contains;
private List<FilterOperatorItem> StringFilterOperators { get; set; } = new List<FilterOperatorItem>() {
new FilterOperatorItem() { Operator = FilterOperator.IsEqualTo, Text = "Is Equal To" },
new FilterOperatorItem() { Operator = FilterOperator.StartsWith, Text = "Starts With" },
new FilterOperatorItem() { Operator = FilterOperator.Contains, Text = "Contains" },
new FilterOperatorItem() { Operator = FilterOperator.DoesNotContain, Text = "Does Not Contain" },
new FilterOperatorItem() { Operator = FilterOperator.EndsWith, Text = "Ends With" },
new FilterOperatorItem() { Operator = FilterOperator.IsContainedIn, Text = "Is Contained In" }
};
private void TreeViewFilterOperatorChanged(FilterOperator newValue)
{
TreeViewFilterOperator = newValue;
if (!string.IsNullOrEmpty(TreeViewFilterValue))
{
FilterTreeView();
}
}
private void TreeViewFilterValueChanged(string newValue)
{
TreeViewFilterValue = newValue;
if (!string.IsNullOrEmpty(TreeViewFilterValue))
{
FilterTreeView();
}
else
{
FilterLog = $"Showing all {FlatData.Count} items.";
FilteredData = FlatData;
}
}
private void FilterTreeView()
{
var request = new DataSourceRequest()
{
Filters = new List<IFilterDescriptor>() {
new FilterDescriptor()
{
Member = nameof(TreeItem.Text),
MemberType = typeof(string),
Operator = TreeViewFilterOperator,
Value = TreeViewFilterValue
}
}
};
var result = FlatData.ToDataSourceResult(request);
var filteredItems = result.Data.Cast<TreeItem>().ToList();
var matchCount = filteredItems.Count;
var addedParents = new List<TreeItem>();
foreach (var item in filteredItems)
{
PopulateParent(item.Id, item.ParentId, filteredItems, addedParents);
}
filteredItems.AddRange(addedParents);
FilterLog = $"Found {matchCount} matches. Showing {filteredItems.Count} out of {FlatData.Count} items.";
FilteredData = filteredItems;
}
private void PopulateParent(int itemId, int? parentId, List<TreeItem> filteredItems, List<TreeItem> addedParents)
{
var parentItem = FlatData.FirstOrDefault(x => x.Id == parentId);
if (parentItem != null)
{
if (filteredItems.FindIndex(x => x.Id == parentItem.Id) == -1 &&
addedParents.FindIndex(x => x.Id == parentItem.Id) == -1)
{
addedParents.Add(parentItem);
}
if (parentItem.ParentId != null)
{
PopulateParent(parentItem.Id, parentItem.ParentId, filteredItems, addedParents);
}
}
}
#endregion Filtering Logic
#region Data Generation and Classes
private int TreeLevels { get; set; } = 3;
private int RootItems { get; set; } = 3;
private int ItemsPerLevel { get; set; } = 2;
private int IdCounter { get; set; } = 1;
private Random Rnd { get; set; } = new Random();
protected override void OnInitialized()
{
FlatData = FilteredData = LoadFlat();
ExpandedItems = FlatData.Where(x => x.HasChildren == true);
}
private List<TreeItem> LoadFlat()
{
List<TreeItem> items = new List<TreeItem>();
PopulateChildren(items, null, 1);
return items;
}
private void PopulateChildren(List<TreeItem> items, int? parentId, int level)
{
var itemCount = level == 1 ? RootItems : ItemsPerLevel;
for (int i = 1; i <= itemCount; i++)
{
var itemId = IdCounter++;
items.Add(new TreeItem()
{
Id = itemId,
Text = $"{itemId} " +
$"{(char)Rnd.Next(65, 91)}{(char)Rnd.Next(65, 91)}{(char)Rnd.Next(65, 91)} " +
$"{Rnd.Next(0, 10)}{Rnd.Next(0, 10)}{Rnd.Next(0, 10)}",
ParentId = parentId,
HasChildren = level < TreeLevels
});
if (level < TreeLevels)
{
PopulateChildren(items, itemId, level + 1);
}
}
}
public class TreeItem
{
public int Id { get; set; }
public string Text { get; set; } = string.Empty;
public int? ParentId { get; set; }
public bool HasChildren { get; set; }
}
public class FilterOperatorItem
{
public FilterOperator Operator { get; set; }
public string Text { get; set; } = string.Empty;
}
#endregion Data Generation and Classes
}
See Also
- TreeView Filtering with Hierarchy Data
- [ToDataSourceResult Method]({%slug common-features-data-binding-onread%}#todatasourceresult-method)
- DataSourceRequest API Reference
- [TreeView Item Template]({%slug components/treeview/templates%})