Hi - just wondering it it's possible to add a doubleClickHandler for a treenode when that node has NO children?
Cheers
Mark
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
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.