From 8f240d9853472c18560d7f6722d30bc7c1a479c3 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 8 Feb 2026 21:32:44 +0800 Subject: [PATCH 01/10] feat(IClosable): add IClosable interface --- .../Components/Dialog/Dialog.razor | 2 +- .../Components/Dialog/Dialog.razor.cs | 3 +++ .../Components/Dialog/DialogOption.cs | 12 ++++++--- .../Components/Modal/IClosable.cs | 25 +++++++++++++++++++ .../Components/Modal/Modal.razor.cs | 8 +++--- 5 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 src/BootstrapBlazor/Components/Modal/IClosable.cs diff --git a/src/BootstrapBlazor/Components/Dialog/Dialog.razor b/src/BootstrapBlazor/Components/Dialog/Dialog.razor index 8d5ab2cb92b..53b5bb91b97 100644 --- a/src/BootstrapBlazor/Components/Dialog/Dialog.razor +++ b/src/BootstrapBlazor/Components/Dialog/Dialog.razor @@ -2,7 +2,7 @@ @inherits BootstrapComponentBase @for (var index = 0; index < DialogParameters.Keys.Count; index++) { diff --git a/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs b/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs index 9a89b66abca..25eb63ffa5a 100644 --- a/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs +++ b/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs @@ -19,6 +19,7 @@ public partial class Dialog : IDisposable private Modal? _modal = null; private Func? _onShownAsync = null; private Func? _onCloseAsync = null; + private Func>? _onClosingAsync = null; private readonly Dictionary, (bool IsKeyboard, bool IsBackdrop, Func? OnCloseCallback)> DialogParameters = []; private Dictionary? _currentParameter; @@ -87,6 +88,8 @@ private async Task Show(DialogOption option) } }; + _onClosingAsync = options.OnClosingAsync; + _isKeyboard = option.IsKeyboard; _isBackdrop = option.IsBackdrop; _isFade = option.IsFade; diff --git a/src/BootstrapBlazor/Components/Dialog/DialogOption.cs b/src/BootstrapBlazor/Components/Dialog/DialogOption.cs index 1fde2fb4105..69a03ece22d 100644 --- a/src/BootstrapBlazor/Components/Dialog/DialogOption.cs +++ b/src/BootstrapBlazor/Components/Dialog/DialogOption.cs @@ -9,7 +9,7 @@ namespace BootstrapBlazor.Components; /// Dialog 对话框组件 /// Dialog component /// -public class DialogOption +public class DialogOption : IClosable { /// /// 获得/设置 关联的 Modal 实例 @@ -192,11 +192,17 @@ public class DialogOption public string? CloseButtonText { get; set; } /// - /// 获得/设置 关闭对话框回调方法 - /// Gets or sets the callback method for closing the dialog + /// /// + [Parameter] public Func? OnCloseAsync { get; set; } + /// + /// + /// + [Parameter] + public Func>? OnClosingAsync { get; set; } + /// /// 获得/设置 是否自动关闭对话框(保存成功后) 默认值为 true /// Gets or sets whether to automatically close the dialog after saving successfully, default is true diff --git a/src/BootstrapBlazor/Components/Modal/IClosable.cs b/src/BootstrapBlazor/Components/Modal/IClosable.cs new file mode 100644 index 00000000000..df1baf0c7d4 --- /dev/null +++ b/src/BootstrapBlazor/Components/Modal/IClosable.cs @@ -0,0 +1,25 @@ +// 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 + +namespace BootstrapBlazor.Components; + +/// +/// 可关闭接口 +/// the closable inerface +/// +public interface IClosable +{ + /// + /// 获得/设置 弹出窗口关闭时的回调委托 + /// Gets or sets the callback delegate when the popup is closed + /// + Func? OnCloseAsync { get; set; } + + /// + /// 关闭之前回调方法 返回 true 时关闭弹窗 返回 false 时阻止关闭弹窗 + /// Callback Method Before Closing. Return true to close, false to prevent closing + /// + Func>? OnClosingAsync { get; set; } +} diff --git a/src/BootstrapBlazor/Components/Modal/Modal.razor.cs b/src/BootstrapBlazor/Components/Modal/Modal.razor.cs index 0d4cf2782c0..607d066b66f 100644 --- a/src/BootstrapBlazor/Components/Modal/Modal.razor.cs +++ b/src/BootstrapBlazor/Components/Modal/Modal.razor.cs @@ -11,7 +11,7 @@ namespace BootstrapBlazor.Components; /// Modal 组件 /// Modal component /// -public partial class Modal +public partial class Modal : IClosable { [Inject] [NotNull] @@ -77,15 +77,13 @@ public partial class Modal public Func? OnShownAsync { get; set; } /// - /// 获得/设置 弹出窗口关闭时的回调委托 - /// Gets or sets the callback delegate when the popup is closed + /// /// [Parameter] public Func? OnCloseAsync { get; set; } /// - /// 关闭之前回调方法 返回 true 时关闭弹窗 返回 false 时阻止关闭弹窗 - /// Callback Method Before Closing. Return true to close, false to prevent closing + /// /// [Parameter] public Func>? OnClosingAsync { get; set; } From 8e79b391536e9379b863154689a66584a306a9f5 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 8 Feb 2026 21:34:15 +0800 Subject: [PATCH 02/10] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E8=AF=8D=E6=8B=BC=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Dialog/DialogOption.cs | 6 +++--- src/BootstrapBlazor/Components/Modal/IClosable.cs | 4 ++-- src/BootstrapBlazor/Components/Modal/Modal.razor.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/BootstrapBlazor/Components/Dialog/DialogOption.cs b/src/BootstrapBlazor/Components/Dialog/DialogOption.cs index 69a03ece22d..eb127fafeca 100644 --- a/src/BootstrapBlazor/Components/Dialog/DialogOption.cs +++ b/src/BootstrapBlazor/Components/Dialog/DialogOption.cs @@ -9,7 +9,7 @@ namespace BootstrapBlazor.Components; /// Dialog 对话框组件 /// Dialog component /// -public class DialogOption : IClosable +public class DialogOption : ICloseable { /// /// 获得/设置 关联的 Modal 实例 @@ -192,13 +192,13 @@ public class DialogOption : IClosable public string? CloseButtonText { get; set; } /// - /// + /// /// [Parameter] public Func? OnCloseAsync { get; set; } /// - /// + /// /// [Parameter] public Func>? OnClosingAsync { get; set; } diff --git a/src/BootstrapBlazor/Components/Modal/IClosable.cs b/src/BootstrapBlazor/Components/Modal/IClosable.cs index df1baf0c7d4..6feb58b5096 100644 --- a/src/BootstrapBlazor/Components/Modal/IClosable.cs +++ b/src/BootstrapBlazor/Components/Modal/IClosable.cs @@ -7,9 +7,9 @@ namespace BootstrapBlazor.Components; /// /// 可关闭接口 -/// the closable inerface +/// the closable interface /// -public interface IClosable +public interface ICloseable { /// /// 获得/设置 弹出窗口关闭时的回调委托 diff --git a/src/BootstrapBlazor/Components/Modal/Modal.razor.cs b/src/BootstrapBlazor/Components/Modal/Modal.razor.cs index 607d066b66f..40b0b284908 100644 --- a/src/BootstrapBlazor/Components/Modal/Modal.razor.cs +++ b/src/BootstrapBlazor/Components/Modal/Modal.razor.cs @@ -11,7 +11,7 @@ namespace BootstrapBlazor.Components; /// Modal 组件 /// Modal component /// -public partial class Modal : IClosable +public partial class Modal : ICloseable { [Inject] [NotNull] @@ -77,13 +77,13 @@ public partial class Modal : IClosable public Func? OnShownAsync { get; set; } /// - /// + /// /// [Parameter] public Func? OnCloseAsync { get; set; } /// - /// + /// /// [Parameter] public Func>? OnClosingAsync { get; set; } From 2e4e749a8fdd4cc9103739e3b8dc682c3a3a73af Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 8 Feb 2026 21:57:08 +0800 Subject: [PATCH 03/10] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=AD=A3=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs b/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs index 25eb63ffa5a..7d555585284 100644 --- a/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs +++ b/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs @@ -88,7 +88,7 @@ private async Task Show(DialogOption option) } }; - _onClosingAsync = options.OnClosingAsync; + _onClosingAsync = option.OnClosingAsync; _isKeyboard = option.IsKeyboard; _isBackdrop = option.IsBackdrop; From feaedd60dfb225811ad810c3b21af25566564a5b Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 8 Feb 2026 21:57:20 +0800 Subject: [PATCH 04/10] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E6=A0=87?= =?UTF-8?q?=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Components/Dialog/DialogOption.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/Dialog/DialogOption.cs b/src/BootstrapBlazor/Components/Dialog/DialogOption.cs index eb127fafeca..d3f583dd64a 100644 --- a/src/BootstrapBlazor/Components/Dialog/DialogOption.cs +++ b/src/BootstrapBlazor/Components/Dialog/DialogOption.cs @@ -194,13 +194,11 @@ public class DialogOption : ICloseable /// /// /// - [Parameter] public Func? OnCloseAsync { get; set; } /// /// /// - [Parameter] public Func>? OnClosingAsync { get; set; } /// From 6f7312f04c18eaf8224ed196e3f00661578d73c6 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 8 Feb 2026 21:57:48 +0800 Subject: [PATCH 05/10] =?UTF-8?q?test:=20=E7=A7=BB=E9=99=A4=20AddMockEnvir?= =?UTF-8?q?onment=20=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Core/TestBase.cs | 2 -- .../IServiceCollectionExtensions.cs | 19 ------------------- 2 files changed, 21 deletions(-) diff --git a/test/UnitTest/Core/TestBase.cs b/test/UnitTest/Core/TestBase.cs index d6b4c46c936..116e9dd6811 100644 --- a/test/UnitTest/Core/TestBase.cs +++ b/test/UnitTest/Core/TestBase.cs @@ -13,8 +13,6 @@ public TestBase() { Context = new BunitContext(); Context.JSInterop.Mode = JSRuntimeMode.Loose; - - Context.Services.AddMockEnvironment(); } public void Dispose() diff --git a/test/UnitTest/Extensions/IServiceCollectionExtensions.cs b/test/UnitTest/Extensions/IServiceCollectionExtensions.cs index 4b9faa37ccf..9371a8de223 100644 --- a/test/UnitTest/Extensions/IServiceCollectionExtensions.cs +++ b/test/UnitTest/Extensions/IServiceCollectionExtensions.cs @@ -4,8 +4,6 @@ // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.DependencyInjection; @@ -29,12 +27,6 @@ public static IServiceCollection AddConfiguration(this IServiceCollection servic return services; } - public static IServiceCollection AddMockEnvironment(this IServiceCollection services) - { - services.AddSingleton(); - return services; - } - public static ILoggingBuilder AddMockLoggerProvider(this ILoggingBuilder builder) { return builder.AddProvider(new TestLoggerProvider()); @@ -61,15 +53,4 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except } } - - class MockEnvironment : IHostEnvironment - { - public string EnvironmentName { get; set; } = "Development"; - - public string ApplicationName { get; set; } = "Test"; - - public string ContentRootPath { get; set; } = "UnitTest"; - - public IFileProvider ContentRootFileProvider { get; set; } = null!; - } } From 63530a75b76bd117d933a856088b276daaa990c5 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 8 Feb 2026 21:58:33 +0800 Subject: [PATCH 06/10] =?UTF-8?q?test:=20=E6=B6=88=E9=99=A4=E8=AD=A6?= =?UTF-8?q?=E5=91=8A=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Core/TestBase.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/UnitTest/Core/TestBase.cs b/test/UnitTest/Core/TestBase.cs index 116e9dd6811..401a29b37db 100644 --- a/test/UnitTest/Core/TestBase.cs +++ b/test/UnitTest/Core/TestBase.cs @@ -17,10 +17,8 @@ public TestBase() public void Dispose() { -#pragma warning disable CA2012 // 由于 bUnit 2.0 继承了 IAsyncDisposable 接口,因此此处调用 DisposeAsync 方法 - Context.DisposeAsync(); -#pragma warning restore CA2012 + Context.DisposeAsync().AsTask().Wait(); GC.SuppressFinalize(this); } } From 98c93230638df7e5bb64bab360c7f1e1ca6a9c82 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sun, 8 Feb 2026 22:13:46 +0800 Subject: [PATCH 07/10] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/DialogTest.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/UnitTest/Components/DialogTest.cs b/test/UnitTest/Components/DialogTest.cs index 79e509b9c98..585abacc9c6 100644 --- a/test/UnitTest/Components/DialogTest.cs +++ b/test/UnitTest/Components/DialogTest.cs @@ -583,6 +583,22 @@ await cut.InvokeAsync(() => dialog.ShowCloseDialog("Clos await cut.InvokeAsync(() => dialog.ShowExceptionDialog(null, new Exception("Test"))); await cut.InvokeAsync(() => modal.Instance.CloseCallback()); #endregion + + // OnClosing + var closing = false; + await cut.InvokeAsync(() => dialog.Show(new DialogOption() + { + OnClosingAsync = () => + { + closing = true; + return Task.FromResult(false); + } + })); + + // 由于返回 false 所以关窗方法被阻止 + await cut.InvokeAsync(() => modal.Instance.Close()); + Assert.True(closing); + await cut.InvokeAsync(() => modal.Instance.CloseCallback()); } private class MockValidateFormDialog : ComponentBase From d4da1116f8a71e3320c3fbfa83550b2639634bee Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 9 Feb 2026 15:25:17 +0800 Subject: [PATCH 08/10] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=20xml=20?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Extensions/PropertyInfoExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Extensions/PropertyInfoExtensions.cs b/src/BootstrapBlazor/Extensions/PropertyInfoExtensions.cs index a13210851eb..ad06c5f7eb5 100644 --- a/src/BootstrapBlazor/Extensions/PropertyInfoExtensions.cs +++ b/src/BootstrapBlazor/Extensions/PropertyInfoExtensions.cs @@ -26,7 +26,7 @@ public static bool IsStatic(this PropertyInfo p) /// /// 判断属性是否可以写入扩展方法 - /// Determines whether the property can be written to extension method + /// Determines whether the property can be written to /// /// public static bool IsCanWrite(this PropertyInfo p) => p.CanWrite && !p.IsInit(); From 89457a2d9493f17f4678e1df22fa92983c9b6927 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 9 Feb 2026 15:26:15 +0800 Subject: [PATCH 09/10] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Utils/BootstrapDynamicComponent.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/BootstrapBlazor/Utils/BootstrapDynamicComponent.cs b/src/BootstrapBlazor/Utils/BootstrapDynamicComponent.cs index d55d33532d5..8fcc6d24036 100644 --- a/src/BootstrapBlazor/Utils/BootstrapDynamicComponent.cs +++ b/src/BootstrapBlazor/Utils/BootstrapDynamicComponent.cs @@ -62,14 +62,14 @@ public RenderFragment RenderEditTemplate(string? modelParameterN { var index = 0; builder.OpenComponent(index++, componentType); + modelParameterName ??= "Model"; + var modelProperty = componentType.GetPropertyByName(modelParameterName); + if (modelProperty.HasParameterAttribute(typeof(TModel))) + { + builder.AddAttribute(index++, modelParameterName, model); + } if (parameters != null) { - modelParameterName ??= "Model"; - var modelProperty = componentType.GetPropertyByName(modelParameterName); - if (modelProperty.HasParameterAttribute(typeof(TModel))) - { - builder.AddAttribute(index++, modelParameterName, model); - } foreach (var p in parameters) { builder.AddAttribute(index++, p.Key, p.Value); From e32d5d5d570ec30b0e1609edcea4285c7a4f304c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Mon, 9 Feb 2026 15:28:04 +0800 Subject: [PATCH 10/10] chore: bump version 10.3.2-beta02 --- src/BootstrapBlazor/BootstrapBlazor.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 5f06f943ea2..3eef7f75957 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 10.3.2-beta01 + 10.3.2-beta02