diff --git a/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor b/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor index 4864578f4b7..e0d480c802f 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor @@ -1,4 +1,4 @@ -@page "/tree-view" +@page "/tree-view" @inject IStringLocalizer Localizer @inject IStringLocalizer LocalizerFoo @@ -15,6 +15,7 @@
  • @((MarkupString)Localizer["TreeViewsTips6"].Value)
  • @((MarkupString)Localizer["TreeViewsTips7"].Value)
  • @((MarkupString)Localizer["TreeViewsTips8"].Value)
  • +
  • @((MarkupString)Localizer["TreeViewsTipsOnBeforeTreeItemClick"].Value)
  • diff --git a/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor.cs index b887c2a04b0..d5ba6a5aaf3 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/TreeViews.razor.cs @@ -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 @@ -319,9 +319,9 @@ private static AttributeItem[] GetAttributes() => { Name = "Items", Description = "menu data set", - Type = "IEnumerable", + Type = "IEnumerable>", ValueList = " — ", - DefaultValue = "new List(20)" + DefaultValue = "new List>(20)" }, new() { @@ -359,7 +359,15 @@ private static AttributeItem[] GetAttributes() => { Name = nameof(TreeView.OnTreeItemClick), Description = "Callback delegate when tree control node is clicked", - Type = "Func", + Type = "Func, Task>", + ValueList = " — ", + DefaultValue = " — " + }, + new() + { + Name = nameof(TreeView.OnBeforeTreeItemClick), + Description = "点击节点前回调方法", + Type = "Func, Task>", ValueList = " — ", DefaultValue = " — " }, @@ -367,7 +375,7 @@ private static AttributeItem[] GetAttributes() => { Name = nameof(TreeView.OnTreeItemChecked), Description = "Callback delegate when tree control node is selected", - Type = "Func", + Type = "Func, Task>", ValueList = " — ", DefaultValue = " — " }, @@ -375,7 +383,7 @@ private static AttributeItem[] GetAttributes() => { Name = nameof(TreeView.OnExpandNodeAsync), Description = "Tree control node expand callback delegate", - Type = "Func", + Type = "Func, Task>", ValueList = " — ", DefaultValue = " — " }, diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index d80f1a4780b..d05ffb8dab7 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -673,6 +673,7 @@ "TreeViewsTips6": "Set whether the node is expanded state through TreeViewItem<TItem>.IsExpand", "TreeViewsTips7": "Set whether the node is selected state through TreeViewItem<TItem>.IsActive", "TreeViewsTips8": "Through TreeViewItem<TItem>.Checked, set whether the node is in checked/single selection state", + "TreeViewsTipsOnBeforeTreeItemClick": "You can prevent a node click by setting the OnBeforeTreeItemClick callback method, and cancel the click action when it returns false.", "TreeViewsTips9": "Step 1: Set the TItem generic model", "TreeViewsTips10": "Step 2: Set Items to get the component data source Note The data source type is IEnumerable<TreeViewItem<TItem>>", "TreeViewsTips11": "Step 3: Set the OnExpandNodeAsync callback delegate to expand the response node to get the child data source collection", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 2774f5f6e0c..2f8f525baf0 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -673,6 +673,7 @@ "TreeViewsTips6": "通过 TreeViewItem<TItem>.IsExpand 设置节点是否 展开 状态", "TreeViewsTips7": "通过 TreeViewItem<TItem>.IsActive 设置节点是否 选中 状态", "TreeViewsTips8": "通过 TreeViewItem<TItem>.Checked 设置节点是否 复选/单选 状态", + "TreeViewsTipsOnBeforeTreeItemClick": "通过设置 OnBeforeTreeItemClick 回调方法可以阻止点击节点动作,返回 false 时取消点击动作", "TreeViewsTips9": "第一步:设置 TItem 泛型模型", "TreeViewsTips10": "第二步:设置 Items 获得组件数据源 注意 数据源类型为 IEnumerable<TreeViewItem<TItem>>", "TreeViewsTips11": "第三步:设置 OnExpandNodeAsync 回调委托响应节点展开获取子项数据源集合", diff --git a/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs b/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs index 9872cf3b150..c4d7db5098b 100644 --- a/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs +++ b/src/BootstrapBlazor/Components/TreeView/TreeView.razor.cs @@ -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 @@ -152,6 +152,12 @@ public partial class TreeView : IModelEqualityComparer [Parameter] public Func, Task>? OnTreeItemClick { get; set; } + /// + /// 获得/设置 点击节点前回调方法 + /// + [Parameter] + public Func, Task>? OnBeforeTreeItemClick { get; set; } + /// /// Gets or sets the callback method when a tree item is checked. /// @@ -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); } @@ -590,24 +596,33 @@ private async Task>> GetChildrenRowAsync(Tree private async Task OnClick(TreeViewItem item) { - _activeItem = item; - if (ClickToggleNode && item.CanTriggerClickNode(IsDisabled, CanExpandWhenDisabled)) + var confirm = true; + 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) diff --git a/test/UnitTest/Components/TreeViewTest.cs b/test/UnitTest/Components/TreeViewTest.cs index 5d91112b9e1..c22d862c4a6 100644 --- a/test/UnitTest/Components/TreeViewTest.cs +++ b/test/UnitTest/Components/TreeViewTest.cs @@ -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>(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 => + { + 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() {