MultiSelect - Refresh Data
The most common reason you would use an ObservableCollection is to make a component (like a grid, treeview, treelist, dropdown) change or react when you change that collection.
When you want to refresh the component data source like that, there are two important framework behaviors you need to be aware of - when ObservableCollection instances fire events, and how to refresh the data of a component when it is not an observable collection.
Sections in this article:
Rebind Method
You can refresh the data of the MultiSelect by using the Rebind
method exposed to the reference of the TelerikMultiSelect. If you have manually defined the OnRead event the business logic defined in its event handler will be executed.
@* Clicking on the Rebind button will delete the first option from the dropdown and refresh the data *@
@using Telerik.DataSource.Extensions
<TelerikButton OnClick="@RebindMultiSelect">Rebind the Multiselect</TelerikButton>
<TelerikMultiSelect TItem="@String"
TValue="@String"
@ref="@MultiSelectRef"
OnRead="@ReadItems"
@bind-Value="@SelectedValues">
</TelerikMultiSelect>
@code{
private TelerikMultiSelect<string, string> MultiSelectRef { get; set; }
private void RebindMultiSelect()
{
if (Options.Count > 0)
{
Options.RemoveAt(0);
}
MultiSelectRef.Rebind();
}
public List<string> SelectedValues { get; set; }
List<string> Options { get; set; } = new List<string>();
async Task ReadItems(MultiSelectReadEventArgs args)
{
await Task.Delay(1000);
args.Data = Options.ToDataSourceResult(args.Request).Data;
}
protected override async Task OnInitializedAsync()
{
Options = new List<string>() { "one", "two", "three" };
}
}
As part of our
3.0.1
release we introduced theRebind
method to the component reference. This would make the rest of the approaches in this article obsolete.
Observable Data
Databound components can benefit from live updates - when the data source collection changes, the components should update to reflect that change. Most data-bound components in the Telerik UI for Blazor suite implement such functionality.
When the Data
of the component is a collection that implements the INotifyCollectionChanged
interface (such as ObservableCollection
), the Telerik components subscribe to its CollectionChanged
event to make live update. This means that adding items, removing items, or clearing the collection updates the components (its .Add()
, .Remove()
and .Clear()
methods).
The Observable collections fire the CollectionChanged
event only when their Add
, Remove
and Clear
methods are called. They do not fire it when you change the value of a field of one of their elements.
@* Add/remove an option to see how the MultiSelect reacts to the change. *@
@using System.Collections.ObjectModel
<h4>Add option</h4>
<TelerikTextBox @bind-Value="@ValuetoAdd"></TelerikTextBox>
<TelerikButton OnClick="@AddOption">Add option</TelerikButton>
<br />
<h4>Remove the last option</h4>
<TelerikButton OnClick="@RemoveOption">Remove the last option</TelerikButton>
<br />
<h4>Options: @Options.Count</h4>
<TelerikMultiSelect Data="@Options" @bind-Value="@TheValues"
TextField="StringRepresentation" ValueField="MyValueField" />
@code{
List<int> TheValues { get; set; } = new List<int>();
string ValuetoAdd { get; set; }
ObservableCollection<OptionsModel> Options { get; set; } = new ObservableCollection<OptionsModel>
{
new OptionsModel { StringRepresentation = "first", MyValueField = 1 },
new OptionsModel { StringRepresentation = "second", MyValueField = 2 },
new OptionsModel { StringRepresentation = "third", MyValueField = 3 }
};
void AddOption()
{
if (!string.IsNullOrWhiteSpace(ValuetoAdd))
{
Options.Add(
new OptionsModel { StringRepresentation = ValuetoAdd, MyValueField = Options.Count + 1 }
);
ValuetoAdd = string.Empty;
}
}
void RemoveOption()
{
if (Options.Count > 0)
{
Options.RemoveAt(Options.Count - 1);
}
}
public class OptionsModel
{
public string StringRepresentation { get; set; }
public int MyValueField { get; set; } // this determines the type of the values list
}
}
If you need to add/remove many items to/from the collection, consider creating a new collection and provide its reference to the data parameter. Thus, the component will re-render only once (when the data collection reference is changed) instead of re-rendering multiple times in response to the Add/Remove events.
New Collection Reference
In Blazor, the framework will fire the OnParametersSet
event of a child component (which is how child components can react to outside changes) only when it can detect a change in the object it receives through the corresponding parameter (like Data
for the data sources of Telerik components). This detection works as follows:
For strings and value types, this happens when their value changes.
-
For reference types (such as data collections like
List
, or anyIEnumerable
, and application-specific objects), this happens when the object reference changes.Thus, you would usually need to create a
new
reference for the view-model field (such asTreeViewData = new List<MyTreeViewItem>(theUpdatedDataCollection);
) when you want the component to update.
@* Add/remove an option or a collection of options to see how the MultiSelect reacts to the change. *@
<h4>Add a new option</h4>
<TelerikTextBox @bind-Value="@ValuetoAdd"></TelerikTextBox>
<TelerikButton OnClick="@AddItem">Add new option</TelerikButton>
<br />
<h4>Remove the last option</h4>
<TelerikButton OnClick="@RemoveItem">Remove last item</TelerikButton>
<br />
<h4>Load a collection of new options</h4>
<TelerikButton OnClick="@LoadNewData">Load new options</TelerikButton>
<br />
<h4>Options: @Options.Count</h4>
<TelerikMultiSelect Data="@Options" @bind-Value="@TheValues"
TextField="StringRepresentation" ValueField="MyValueField" />
@code{
List<int> TheValues { get; set; }
string ValuetoAdd { get; set; }
List<OptionsModel> Options { get; set; } = new List<OptionsModel>
{
new OptionsModel { StringRepresentation = "first", MyValueField = 1 },
new OptionsModel { StringRepresentation = "second", MyValueField = 2 },
new OptionsModel { StringRepresentation = "third", MyValueField = 3 }
};
void AddItem()
{
if (!string.IsNullOrWhiteSpace(ValuetoAdd))
{
Options.Add(new OptionsModel { StringRepresentation = ValuetoAdd, MyValueField = Options.Count + 1 });
Options = new List<OptionsModel>(Options);
ValuetoAdd = string.Empty;
}
}
void RemoveItem()
{
if (Options.Count > 0)
{
Options.RemoveAt(Options.Count - 1);
Options = new List<OptionsModel>(Options);
}
}
void LoadNewData()
{
var newData = new List<OptionsModel>
{
new OptionsModel { StringRepresentation = "fourth", MyValueField = 4 },
new OptionsModel { StringRepresentation = "fifth", MyValueField = 5 },
new OptionsModel { StringRepresentation = "sixth", MyValueField = 6 }
};
Options = new List<OptionsModel>(newData);
Console.WriteLine("New options collection loaded.");
}
public class OptionsModel
{
public string StringRepresentation { get; set; }
public int MyValueField { get; set; } // this determines the type of the values list
}
}
Update Value
The Value
parameter also accepts a collection but it does not support observable data. If you want to change the Value, make sure you are providing a collection of items that are included in the data source (not random ones).
<h4>Set or change selected values</h4>
<TelerikButton OnClick="@SetSelected">Set selected</TelerikButton>
<br />
<h4>Clear selected values</h4>
<TelerikButton OnClick="@ClearSelected">Clear selected</TelerikButton>
<br />
@if (TheValues.Count > 0)
{
<ul>
@foreach (var item in TheValues)
{
<li>@item</li>
}
</ul>
}
<TelerikMultiSelect Data="@Options" @bind-Value="@TheValues"
TextField="StringRepresentation" ValueField="MyValueField" />
@code{
List<int> TheValues { get; set; } = new List<int>();
string ValuetoAdd { get; set; }
void SetSelected()
{
TheValues = Options.Select(x => x.MyValueField).Skip(1).Take(2).ToList();
}
void ClearSelected()
{
TheValues = new List<int>();
}
List<OptionsModel> Options { get; set; } = new List<OptionsModel>
{
new OptionsModel { StringRepresentation = "first", MyValueField = 1 },
new OptionsModel { StringRepresentation = "second", MyValueField = 2 },
new OptionsModel { StringRepresentation = "third", MyValueField = 3 },
new OptionsModel { StringRepresentation = "fourth", MyValueField = 4 },
new OptionsModel { StringRepresentation = "fifth", MyValueField = 5 },
new OptionsModel { StringRepresentation = "sixth", MyValueField = 6 }
};
public class OptionsModel
{
public string StringRepresentation { get; set; }
public int MyValueField { get; set; } // this determines the type of the values list
}
}