Hi! I'm implementing a dropdown list with multi-select checkboxes and grouping as described in your demo/documentation. When I click the checkbox everything is good. When I click the text or any whitespace in the dropdown list I get 1 of 2 errors.
Error: System.InvalidCastException: Unable to cast object of type 'MyModel' to type 'System.Collections.IEnumerable'.
at Radzen.DropDownBase1.get_HasValue() at Radzen.DataBoundFormComponent
1.GetClassList(String className)
at Radzen.Blazor.RadzenDropDown1.GetComponentCssClass() at Radzen.RadzenComponent.GetCssClass() at Radzen.Blazor.RadzenDropDown
1.BuildRenderTree(RenderTreeBuilder __builder)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
Error: System.InvalidCastException: Unable to cast object of type ''MyModel' ' to type 'System.Collections.Generic.IEnumerable1[System.Int32]'. at Radzen.DropDownBase
1.SelectItem(Object item, Boolean raiseChange)
at Radzen.Blazor.RadzenDropDown1.OnSelectItem(Object item, Boolean isFromKey) at Radzen.Blazor.RadzenDropDown
1.OnSelectItemInternal(Object item, Boolean isFromKey)
at Radzen.Blazor.RadzenDropDownItem`1.SelectItem(MouseEventArgs args, Boolean isclick)
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
Here's the markup:
<RadzenDropDown @bind-Value=selectedFranchises TValue="IEnumerable<int>" AllowClear="true" Name="ddlFranchises" @ref=dd
Data=@filteredFranchises Style="width: 100%;" ItemRender="ItemRender">
<HeaderTemplate>
<RadzenStack Orientation="Radzen.Orientation.Horizontal" class="rz-multiselect-header rz-dropdown-filter-container rz-helper-clearfix">
<RadzenCheckBox TValue="bool?" TriState=false Value="@CheckIfAllSelected()" Change="@SelectAll" title="@(dd.SelectAllText)" />
<input id="@($"{dd.SearchID}")" @oninput=@Search placeholder="Search..." class="rz-inputtext" role="textbox" type="text"
aria-label="@(dd.SearchAriaLabel)" style="margin-inline-start:0.5rem;width:100%;border:0;background:transparent;" />
</RadzenStack>
</HeaderTemplate>
<Template>
<RadzenCheckBox TValue="bool?" TriState=false Value="@IsGroupSelected(context)" Change="@(args => SelectGroup(args, (MyModel)context))" />
<RadzenLabel Style=@($"margin-inline-start: 0.5rem; font-weight: {(context.Family != null ? "bold" : "normal")}")
Text="@(context.Family ?? context.Make)" onclick="event.target.previousElementSibling.querySelector('.rz-chkbox-box').click()" />
</Template>
<ValueTemplate>
@string.Join(", ", filteredFranchises.Where(f => selectedFranchises.Contains(f.MakeId) == true).Take(dd.MaxSelectedLabels).Select(f => f.Make))
</ValueTemplate>
</RadzenDropDown>
Here's the code behind:
IEnumerable<int> selectedFranchises;
RadzenDropDown<IEnumerable<int>> dd;
IEnumerable<MyModel> allFranchises;
IEnumerable<MyModel> filteredFranchises;
string franchiseSearch;
public class MyModel
{
public int MakeId { get; set; }
public string Make { get; set; }
public string Family { get; set; }
public IEnumerable<MyModel> RelatedMakes { get; set; } = Enumerable.Empty<MyModel>();
}
void ItemRender(DropDownItemRenderEventArgs<IEnumerable<int>> args)
{
try
{
// Use this code to prevent default item selection.
args.Attributes.Add("disabled", "true");
args.Attributes.Add("style", $"opacity:1;{(((MyModel)args.Item).Family == null ? "margin-inline-start:1rem" : "")}");
}
catch (Exception ex)
{
//error handling
}
}
bool? CheckIfAllSelected()
{
try
{
return filteredFranchises?.All(g => selectedFranchises?.Contains(g.MakeId) == true) == true ? (filteredFranchises?.Any() == true ? true : false) :
filteredFranchises?.Any(g => selectedFranchises?.Contains(g.MakeId) == true) == true ? null : false;
}
catch (Exception ex)
{
//error handling
}
}
void SelectAll(bool? value)
{
try
{
selectedFranchises = value == true ? filteredFranchises.Select(g => g.MakeId) : Enumerable.Empty<int>();
}
catch (Exception ex)
{
//error handling
}
}
void SelectGroup(bool? value, MyModel group)
{
//logic to handle selecting group
try
{
var newValues = selectedFranchises ?? Enumerable.Empty<int>();
var items = group.Family!= null ? group.RelatedMakes.Select(i => i.MakeId) : new int[] { group.MakeId };
selectedFranchises = value == true ? newValues.Concat(items) : newValues.Except(items);
}
catch (Exception ex)
{
//error handling
}
}
bool? IsGroupSelected(MyModel group)
{
try
{
if (group.Family != null)
{
return group.RelatedMakes.Any() && group.RelatedMakes.All(i => selectedFranchises?.Contains(i.MakeId) == true) ? true :
group.RelatedMakes.Any(i => selectedFranchises?.Contains(i.MakeId) == true) ? null : false;
}
return selectedFranchises?.Contains(group.MakeId) == true;
}
catch (Exception ex)
{
//error handling
}
}
void Search(ChangeEventArgs args)
{
franchiseSearch = $"{args.Value}";
Populate();
}
void Populate()
{
try
{
filteredFranchises = allFranchises
.Where(x => x.Family!= null &&
(string.IsNullOrEmpty(franchiseSearch) || (x.Family.Contains(franchiseSearch, StringComparison.CurrentCultureIgnoreCase)
|| x.Make.Contains(franchiseSearch, StringComparison.CurrentCultureIgnoreCase)
)
)
).ToList()
.OrderBy(g => g.Family)
.GroupBy(g => g.Family)
.SelectMany(i => new MyModel[] { new() { Family = i.Key, RelatedMakes = i } }
.Concat(i.Select(x => new MyModel() { Make = x.Make, MakeId = x.MakeId }))
.OrderBy(x => x.Make)
);
}
catch (Exception ex)
{
//error handling
}
The allFranchises is loaded up using an ef query and the data looks good. The dropdown looks as expected and functions fine as long as a user doesn't click the text or the white space. I've put breakpoints in every method's try and catch to see if I can catch an error and none of them get hit. I've tried removing parts of the markup like the onclick event and such to see if I could isolate what bit of code is not working 100% of the time, but it looks like it's behind the scenes stuff.
I'm guessing I'm overlooking something with how I set up the markup or with the populate, but I think it matches the documentation (with the exception of data types). Any advice would be greatly appreciated.