Doubleclick on TreeNode

Hi - just wondering it it's possible to add a doubleClickHandler for a treenode when that node has NO children?

Cheers

Mark

Hi @markgr,

This should be doable by using the Template property. Similar to our Files and directories section from the tree demo.

Hi - I was trying to use the Template but one part of it defeated me - it could be my lack of understanding how Fragments work in Blazor however.

So in my BASE class I inject TWO services

[Inject] HttpClient Http { get; set; }
[Inject] NotificationService NotificationService { get; set; }

The Latter is a service which allows me to get a JWT token out of a cookie.

During the OnInitializedAsync method - I essentially do this


Psuedo code

Get the JWT token from the NotificationService
Use the HTTP client to request from an API running on a Server to return a list of entries

Set the returned entries to be the data for the tree

So far so good - in my Tree I define that the RadzenTreeLevel should use a Template

<RadzenTree Style="width: 100%; height: 800px " Data="@List" Expand="@LoadPath">
                <RadzenTreeLevel Text="@GetTextForNode"
                                 HasChildren="@((item) => DoesChildHaveChildren(item))"
                                 Template="@ItemTemplate" />
                </RadzenTree>

And the ItemTemplate is defined as such

          var currentPath = context.Value as VaultTreeList;

            builder.OpenComponent<RadzenIcon>(0);
            builder.AddAttribute(1, "Icon", currentPath.HasChildren ? "folder" : "account_balance");
            if (currentPath.HasChildren == false)
            {
                builder.AddAttribute(2, "Style", "margin-left: 24px");
            }
            // Set some margin if the current data item is a file (!isDirectory)
            builder.CloseComponent();

            // Append the current item text
            builder.AddContent(3, context.Text);

This is pretty much as in your demo (VaultTreeList is a class with the details I need to display )- again so far so good.

I tried to add a button as well to each node so that when a user clicked the button I could respond to that event

so

Template code as above and then this....

if (currentPath.HasChildren == false)
            {                
                builder.OpenComponent<RadzenButton>(4);
                builder.AddAttribute(5, "Style", "margin-left: 24px");
                builder.AddAttribute(6, "Text", "Open");
                builder.AddAttribute(7, "Icon", "menu");
                
            //    //builder.AddAttribute(8, "Click", EventCallback.Factory.Create<MouseEventArgs>(context, (args) => OpenItem(args, currentPath.Path)));
                builder.AddAttribute(9, "ButtonStyle", ButtonStyle.Light);

                builder.CloseComponent();
            }

The handler was defined like this

        async Task OpenItem(MouseEventArgs args, string path)
        {
            Console.WriteLine($"Path To pen = {path}");

            // This does not compile unless I make this function static
        }

The problem line is the one with attribute 8 - when I try and add a handler to the button - I get a compile error stating that the function needs to be static...

If I do that I no longer have access to the Injected HTTP and NotificationService as they are only available to instances of the class and not to the Object itself.

Obviously I am doing something wrong - can you advise how to add a Click handler using a RenderFragment from a button added to the tree - I spent all day playing and could not find out how to do it....

Many thanks in advance

Mark.

You can check the Blazor generated code of some of your other pages that has Click handler of a Button defined. It is in the obj\Debug\netcoreapp3.1\Razor\Pages directory. Here is one for example:

__builder2.AddAttribute(25, "Click", Microsoft.AspNetCore.Components.CompilerServices.RuntimeHelpers.TypeCheck<Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.Web.MouseEventArgs>>(Microsoft.AspNetCore.Components.EventCallback.Factory.Create<Microsoft.AspNetCore.Components.Web.MouseEventArgs>(this, 
 (args) => Click(args, "Button with icon"))));

And I am pretty sure the TypeCheck call can be omitted:

EventCallback.Factory.Create<MouseEventArgs>(this, 
  (args) => Click(args, "Button with icon")
)

I tried that too :slight_smile:

There is no 'this' in the current context .....

As we are using a Lambda expression

protected RenderFragment<RadzenTreeItem> VaultItemTemplate = (context) => builder =>

The only thing we have access to is to the treeitem fragment itself (the 'context')

I assume I need to create a function which is a method of the class which returns a RenderFragment instead ?

I tried writing one but again my lack of Blazor coding was against me - I can see how to use a RenderBuiilder but not how it returns a fragment of correct type?!?

I think this is used as the context parameter in this case. It can be anything really (even null).

I think the ‘this’ is valid as it’s being done in the override of BuildRenderTree

The issue still remains that adding a non static function handler to the button generates a code error....

I’ll keep looking

Well our demo uses a static initializer - hence this is not available. One can set the template in an instance method though - OnInitialized. Here is something that works (it also relies on the Blazor compiler to generate the RenderTreeBuilder calls):

protected override void OnInitialized()
{
   FileOrFolderTemplate = (context) => 
   {
      string path = context.Value as string;
      bool isDirectory = Directory.Exists(path);
      return @<span @onclick="(args) => OnClick(path)">@context.Text</span>;
   };
 }

 void OnClick(string path)
 {
        Console.WriteLine(path);
 }

Interesting - I’ll give it a try - many thanks

Hello!
i was running into a similar question (how to add buttons to the RadzenTree component) and ended up with the same error message "compile error stating that the function needs to be static". this thread was very helpful to solve it. here is some code that compiles and works, maybe it is beneficial for other people.

variant 1: with template builder

<RadzenTree @ref="_tree" Data="@treeDataSource" Change="@OnTreeSelectionChange" Expand="OnTreeNodeExpand" Style="min-width: 300px; height: 100%;">
    <RadzenTreeLevel TextProperty="Name" ChildrenProperty="Categories" Template="@TreeTemplate" />
    <RadzenTreeLevel TextProperty="Name" Template="@TreeTemplate" />
</RadzenTree>

protected override async Task OnInitializedAsync()
{
    _mode = NavTreeMode.Navigate;
    CreateRenderTemplates();
    await loadData();
}

    // template for generic tree node. 
    private RenderFragment<RadzenTreeItem> TreeTemplate;
    private void CreateRenderTemplates()
    {
        TreeTemplate = (context) => builder =>
        {
            if (context.Value is Group)
            {
                builder.AddContent(0, (context.Value as Group).Name);
                builder.OpenComponent<RadzenButton>(1);
                builder.AddAttribute(2, "Size", ButtonSize.Small);
                builder.AddAttribute(3, "Icon", "clear");
                builder.AddAttribute(4, "Click", EventCallback.Factory.Create<MouseEventArgs>(this, (() => DeleteButtonHandler(context.Value as Group))));
                builder.AddEventStopPropagationAttribute(5, "onclick", true);
                builder.CloseComponent();
            }
            else if (context.Value is Category)
            {
                var c = context.Value as Category;
                builder.AddContent(0, c.Name);
            }
            else
            {
            }
        };
    }

private async void DeleteButtonHandler(Group g)
{
    // do something
}

variant 2: markup only

<RadzenTree @ref="_tree" Data="@treeDataSource" Change="@OnTreeSelectionChange" Expand="OnTreeNodeExpand" Style="min-width: 300px; height: 100%;">
    <RadzenTreeLevel TextProperty="Name" ChildrenProperty="Categories">
        <Template>
            @((context.Value as Group).Name) -
                <RadzenButton Size="ButtonSize.Small" Icon="clear" Click="(() => DeleteButtonHandler(context.Value as Group))" @onclick:stopPropagation="true" />
        </Template>
    </RadzenTreeLevel>
    <RadzenTreeLevel TextProperty="Name"  />
</RadzenTree>

private async void DeleteButtonHandler(Group g)
{
    // do something
}

do note that variant 2, for some reason, only worked for me after adding the onclick:stopPropagation attribute. but it makes sense anyway.