Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "/tree-view"
@page "/tree-view"
@inject IStringLocalizer<TreeViews> Localizer
@inject IStringLocalizer<Foo> LocalizerFoo

Expand All @@ -15,6 +15,7 @@
<li>@((MarkupString)Localizer["TreeViewsTips6"].Value)</li>
<li>@((MarkupString)Localizer["TreeViewsTips7"].Value)</li>
<li>@((MarkupString)Localizer["TreeViewsTips8"].Value)</li>
<li>@((MarkupString)Localizer["TreeViewsTipsOnBeforeTreeItemClick"].Value)</li>
</ul>
</Tips>

Expand Down
20 changes: 14 additions & 6 deletions src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
Expand Down Expand Up @@ -319,9 +319,9 @@ private static AttributeItem[] GetAttributes() =>
{
Name = "Items",
Description = "menu data set",
Type = "IEnumerable<TreeViewItem>",
Type = "IEnumerable<TreeViewItem<TItem>>",
ValueList = " — ",
DefaultValue = "new List<TreeViewItem>(20)"
DefaultValue = "new List<TreeViewItem<TItem>>(20)"
},
new()
{
Expand Down Expand Up @@ -359,23 +359,31 @@ private static AttributeItem[] GetAttributes() =>
{
Name = nameof(TreeView<string>.OnTreeItemClick),
Description = "Callback delegate when tree control node is clicked",
Type = "Func<TreeViewItem, Task>",
Type = "Func<TreeViewItem<TItem>, Task>",
ValueList = " — ",
DefaultValue = " — "
},
new()
{
Name = nameof(TreeView<string>.OnBeforeTreeItemClick),
Description = "点击节点前回调方法",
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Description field contains Chinese text while other attributes in this file use English descriptions. This is inconsistent with the rest of the file where descriptions are in English. The Description should be translated to English to maintain consistency.

Suggested change
Description = "点击节点前回调方法",
Description = "Callback delegate before tree control node is clicked",

Copilot uses AI. Check for mistakes.
Type = "Func<TreeViewItem<TItem>, Task<bool>>",
ValueList = " — ",
DefaultValue = " — "
},
new()
{
Name = nameof(TreeView<string>.OnTreeItemChecked),
Description = "Callback delegate when tree control node is selected",
Type = "Func<TreeViewItem, Task>",
Type = "Func<TreeViewItem<TItem>, Task>",
ValueList = " — ",
DefaultValue = " — "
},
new()
{
Name = nameof(TreeView<string>.OnExpandNodeAsync),
Description = "Tree control node expand callback delegate",
Type = "Func<TreeViewItem, Task>",
Type = "Func<TreeViewItem<TItem>, Task>",
ValueList = " — ",
DefaultValue = " — "
},
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Server/Locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,7 @@
"TreeViewsTips6": "Set whether the node is <b>expanded</b> state through <code>TreeViewItem&lt;TItem&gt;.IsExpand</code>",
"TreeViewsTips7": "Set whether the node is <b>selected</b> state through <code>TreeViewItem&lt;TItem&gt;.IsActive</code>",
"TreeViewsTips8": "Through <code>TreeViewItem&lt;TItem&gt;.Checked</code>, set whether the node is in <b>checked/single selection</b> state",
"TreeViewsTipsOnBeforeTreeItemClick": "You can prevent a node click by setting the <code>OnBeforeTreeItemClick</code> callback method, and cancel the click action when it returns <code>false</code>.",
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The English translation is somewhat redundant with "prevent" and "cancel" conveying the same meaning. The sentence can be simplified to be more concise and clear.

Suggested change
"TreeViewsTipsOnBeforeTreeItemClick": "You can prevent a node click by setting the <code>OnBeforeTreeItemClick</code> callback method, and cancel the click action when it returns <code>false</code>.",
"TreeViewsTipsOnBeforeTreeItemClick": "Use the <code>OnBeforeTreeItemClick</code> callback to cancel a node click by returning <code>false</code>.",

Copilot uses AI. Check for mistakes.
"TreeViewsTips9": "Step 1: Set the <code>TItem</code> generic model",
"TreeViewsTips10": "Step 2: Set <code>Items</code> to get the component data source <b>Note</b> The data source type is <code>IEnumerable&lt;TreeViewItem&lt;TItem&gt;&gt;</code>",
"TreeViewsTips11": "Step 3: Set the <code>OnExpandNodeAsync</code> callback delegate to expand the response node to get the child data source collection",
Expand Down
1 change: 1 addition & 0 deletions src/BootstrapBlazor.Server/Locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,7 @@
"TreeViewsTips6": "通过 <code>TreeViewItem&lt;TItem&gt;.IsExpand</code> 设置节点是否 <b>展开</b> 状态",
"TreeViewsTips7": "通过 <code>TreeViewItem&lt;TItem&gt;.IsActive</code> 设置节点是否 <b>选中</b> 状态",
"TreeViewsTips8": "通过 <code>TreeViewItem&lt;TItem&gt;.Checked</code> 设置节点是否 <b>复选/单选</b> 状态",
"TreeViewsTipsOnBeforeTreeItemClick": "通过设置 <code>OnBeforeTreeItemClick</code> 回调方法可以阻止点击节点动作,返回 <code>false</code> 时取消点击动作",
"TreeViewsTips9": "第一步:设置 <code>TItem</code> 泛型模型",
"TreeViewsTips10": "第二步:设置 <code>Items</code> 获得组件数据源 <b>注意</b> 数据源类型为 <code>IEnumerable&lt;TreeViewItem&lt;TItem&gt;&gt;</code>",
"TreeViewsTips11": "第三步:设置 <code>OnExpandNodeAsync</code> 回调委托响应节点展开获取子项数据源集合",
Expand Down
43 changes: 29 additions & 14 deletions src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone
Expand Down Expand Up @@ -152,6 +152,12 @@ public partial class TreeView<TItem> : IModelEqualityComparer<TItem>
[Parameter]
public Func<TreeViewItem<TItem>, Task>? OnTreeItemClick { get; set; }

/// <summary>
/// 获得/设置 点击节点前回调方法
/// </summary>
[Parameter]
public Func<TreeViewItem<TItem>, Task<bool>>? OnBeforeTreeItemClick { get; set; }

/// <summary>
/// Gets or sets the callback method when a tree item is checked.
/// </summary>
Expand Down Expand Up @@ -379,7 +385,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
await InvokeVoidAsync("scroll", Id, ScrollIntoViewOptions);
}

if(!firstRender && AllowDrag)
if (!firstRender && AllowDrag)
{
await InvokeVoidAsync("resetTreeViewRow", Id);
}
Expand Down Expand Up @@ -590,24 +596,33 @@ private async Task<IEnumerable<IExpandableNode<TItem>>> GetChildrenRowAsync(Tree

private async Task OnClick(TreeViewItem<TItem> item)
{
_activeItem = item;
if (ClickToggleNode && item.CanTriggerClickNode(IsDisabled, CanExpandWhenDisabled))
var confirm = true;
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable name 'confirm' is misleading in this context. Since the callback is named 'OnBeforeTreeItemClick' and returns a boolean to indicate whether the click should proceed, a more descriptive name would be 'shouldProceed' or 'canProceed' to better convey the intent that this controls whether the click action continues.

Copilot uses AI. Check for mistakes.
if (OnBeforeTreeItemClick != null)
{
await OnToggleNodeAsync(item);
confirm = await OnBeforeTreeItemClick(item);
}

if (OnTreeItemClick != null)
if (confirm)
{
await OnTreeItemClick(item);
}
_activeItem = item;
if (ClickToggleNode && item.CanTriggerClickNode(IsDisabled, CanExpandWhenDisabled))
{
await OnToggleNodeAsync(item);
}

if (ShowCheckbox && ClickToggleCheck)
{
var state = ToggleCheckState(item.CheckedState);
await OnCheckStateChanged(item, state);
}
if (OnTreeItemClick != null)
{
await OnTreeItemClick(item);
}

StateHasChanged();
if (ShowCheckbox && ClickToggleCheck)
{
var state = ToggleCheckState(item.CheckedState);
await OnCheckStateChanged(item, state);
}

StateHasChanged();
}
}

private async Task OnEnterAsync(string? searchText)
Expand Down
23 changes: 23 additions & 0 deletions test/UnitTest/Components/TreeViewTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,29 @@ public async Task ClickToggleNode_Ok()
Assert.True(clicked);
}

[Fact]
public async Task OnBeforeTreeItemClick_Ok()
{
var clicked = false;
var cut = Context.Render<TreeView<TreeFoo>>(pb =>
{
pb.Add(a => a.ClickToggleNode, true);
pb.Add(a => a.Items, TreeFoo.GetTreeItems());
pb.Add(a => a.OnTreeItemClick, node =>
{
clicked = true;
return Task.CompletedTask;
});
pb.Add(a => a.OnBeforeTreeItemClick, node =>
Comment thread
ArgoZhang marked this conversation as resolved.
{
return Task.FromResult(false);
});
});
var node = cut.FindAll(".tree-node").Skip(1).First();
await cut.InvokeAsync(() => node.Click());
Assert.False(clicked);
}

[Fact]
public void KeyAttribute_Ok()
{
Expand Down
Loading