RadzenTree question ChildrenProperty

Hi all!

On my blazor server page i'm trying to get a RadzenTree working with Checkboxes, I retrieve my data via EF Core but i think this does not work together with the ChildrenProperty on RadzenTreeLevel.

On the following page I want to retrieve user groups for the active user via EF Core and also group members for the corresponding groups. When debugging I see that all required objects and structures are there. But when expanding the tree i get an exception.

Loading the page and showing the first level of the tree works:

But when clicking the expand icon the exception follows:

I don't think the data is false:
usergroups result

Here is the code of the page:

@page "/calendar"
@using X.DbAccess.Services
@using X.Models
@using X.WebApp.Components.Dialogs
@using System.Collections.ObjectModel

@inject UserService _userService;
@inject NavigationManager _navigationManager;
@inject ProtectedSessionStorage _sessionStore;
@inject DialogService _dialogService;

<PageTitle>Calendar</PageTitle>

<div class="container-fluid">
    <div class="row my-5">
        <div class="col-lg-6 offset-lg-3">
            <RadzenCard>
                <RadzenTree AllowCheckBoxes="true" @bind-CheckedValues=@CheckedValues Style="width: 100%; height: 300px" Data=@userGroups
                            ItemRender="@TreeItemRender">
                    <RadzenTreeLevel TextProperty="Name" ChildrenProperty="GroupMembers" />
                    <RadzenTreeLevel TextProperty="Id" HasChildren=@(gm => false)></RadzenTreeLevel>
                </RadzenTree>
            </RadzenCard>
        </div>
    </div>
</div>


@code 
{
    IEnumerable<object> checkedValues;
    IEnumerable<UserGroup> userGroups;
    UserGroup firstGroup;

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        //Get logged in user details from session storage
        var activeUser = await _sessionStore.GetAsync<User>("userDetails");

        //Get groups for logged in user
        userGroups = await _userService.GetUserGroups(activeUser.Value.Id);

        //Set first group
        firstGroup = userGroups.FirstOrDefault();

        //Set members of first group
        checkedValues = firstGroup.GroupMembers;

    }

    void TreeItemRender(TreeItemRenderEventArgs args)
    {
        if (args.Value == firstGroup && !args.Data.Cast<object>().Any())
        {
            args.Checked = null;
        }
    }


    IEnumerable<object> CheckedValues
    {
        get => checkedValues;
        set
        {
            checkedValues = value;
            if (checkedValues != null)
            {
                //console.Log($"CheckedValues Changed {string.Join(Environment.NewLine, value.Select(GetText))}");
            }
        }
    }

}

And this is the EF Core method (GetUserGroups)

 public async Task<IEnumerable<UserGroup>?> GetUserGroups(int userId) 
 {
     try
     {
         using (var context = await _dbContextFactory.CreateDbContextAsync()) 
         {
             IEnumerable<UserGroup> userGroups = await context.UserGroups
                 .Include(gm => gm.GroupMembers)
                 .Where(p => p.UserId == userId)
                 .ToListAsync();

             if (userGroups != null && userGroups.Count() > 0) 
             {
                 return userGroups;
             }
             return null;

         }
     }
     catch (Exception ex)
     {
         throw;
     }
 }

What am i doing wrong or missing here?

Ok, so I found what was wrong.

The "TextProperty" -> "Id" is no string, and on this it fails.
Next problem is that the class GroupMembers has no fields of type string.

    public class GroupMember
    {
        //EF CORE REQUIREMENTS
        [Key]
        public int Id { get; set; }  //PK for dbo.GroupMembers

        //For link to UserGroup
        public int? UserGroupId { get; set; } // Optional foreign key property to UserGroups

        public UserGroup? UserGroup { get; set; } // Optional reference navigation to principal UserGroup

        //For link to User
        public int? UserId { get; set; } // Optional foreign key property to Users

        public User? User { get; set; } // Optional reference navigation to principal User
    }

So i'm not sure how to procceed. I want to be able to use User.Fullname as TextProperty but this also fails. Any ideas?

If your class is partial you can extend it with a [NotMapped] property representing desired other property value as string.

Thank you, that works. Is there also a way to use User.Fullname because ultimately that is what I want to display.

TextProperty="User.Fullname" obviously does not work

No, dot notation is not supported for this property. You can use the technique described in my previous post or template if you need to access sub property value. We accept also pull requests!

Thank you, I got it working by using a Template:

                    <RadzenTree AllowCheckBoxes="true" @bind-CheckedValues=@CheckedValues Style="width: 100%; height: 300px" Data=@userGroups>
                        <RadzenTreeLevel TextProperty="Name" ChildrenProperty="GroupMembers" Template="@groupTemplate" />
                        <RadzenTreeLevel TextProperty="" HasChildren=@(usr => false) Template="@userTemplate"/>
                    </RadzenTree>

    RenderFragment<RadzenTreeItem> groupTemplate = (context) => builder =>
    {
        UserGroup userGroup = context.Value as UserGroup;
        builder.AddContent(0, userGroup.Name);
    };


    RenderFragment<RadzenTreeItem> userTemplate = (context) => builder =>
    {
        GroupMember groupMember = context.Value as GroupMember;
        builder.AddContent(0, groupMember.User.Fullname);
    };

Next step is to add a button on each UserGroup level that opens a dialogue with a picklist.
I'm really new to using RenderFragments but are there any examples or demo's to investigate how to add this button?: