diff --git a/src/BootstrapBlazor.Shared/Data/CustomDynamicObjectData.cs b/src/BootstrapBlazor.Shared/Data/CustomDynamicColumnsObjectData.cs
similarity index 50%
rename from src/BootstrapBlazor.Shared/Data/CustomDynamicObjectData.cs
rename to src/BootstrapBlazor.Shared/Data/CustomDynamicColumnsObjectData.cs
index 9aa6472a611590dcad96fe1829495077bc9d3d9d..420d4a44c5de7a1c0074034b67a10fc7bd64fc91 100644
--- a/src/BootstrapBlazor.Shared/Data/CustomDynamicObjectData.cs
+++ b/src/BootstrapBlazor.Shared/Data/CustomDynamicColumnsObjectData.cs
@@ -7,49 +7,37 @@ namespace BootstrapBlazor.Shared;
///
///
///
-public class BootstrapBlazorDynamicObjectData : IDynamicObject
+public class CustomDynamicColumnsObjectData : DynamicColumnsObject
{
///
///
///
- public string Fix { get; set; } = "";
+ public string? Fix { get; set; }
///
///
///
- public Dictionary Dynamic { get; set; }
-
- ///
- ///
- ///
- public Guid DynamicObjectPrimaryKey { get => new(Fix); set { } }
+ public CustomDynamicColumnsObjectData() : this("", new()) { }
///
///
///
///
///
- public BootstrapBlazorDynamicObjectData(string fix, Dictionary data)
+ public CustomDynamicColumnsObjectData(string? fix, Dictionary data)
{
Fix = fix;
- Dynamic = data;
+ Columns = data;
}
- ///
- ///
- ///
- public BootstrapBlazorDynamicObjectData() : this("", new()) { }
-
///
///
///
///
///
- public object? GetValue(string propertyName)
+ public override object? GetValue(string propertyName)
{
- if (propertyName == nameof(Fix))
- return Fix;
- return Dynamic.TryGetValue(propertyName, out string? v) ? v : "";
+ return propertyName == nameof(Fix) ? Fix : base.GetValue(propertyName);
}
///
@@ -57,12 +45,10 @@ public class BootstrapBlazorDynamicObjectData : IDynamicObject
///
///
///
- public void SetValue(string propertyName, object? value)
+ public override void SetValue(string propertyName, object? value)
{
- if (value is not string str)
- return;
if (propertyName == nameof(Fix))
- Fix = str;
- Dynamic[propertyName] = str;
+ Fix = value?.ToString();
+ Columns[propertyName] = value;
}
}
diff --git a/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs b/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs
index 63f72d56913da4c306d66dcf43cbef6ff6e7b8d9..91b7d3c7b98da7a47a318fbd388c664d107c3177 100644
--- a/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs
+++ b/src/BootstrapBlazor.Shared/Data/CustomDynamicData.cs
@@ -12,14 +12,14 @@ namespace BootstrapBlazor.Shared;
public class CustomDynamicData : System.Dynamic.DynamicObject
{
///
- ///
+ /// 获得/设置 固定列
///
public string Fix { get; set; } = "";
///
/// 存储每列值信息 Key 列名 Value 为列值
///
- public Dictionary Dynamic { get; set; }
+ public Dictionary Columns { get; set; }
///
///
@@ -29,7 +29,7 @@ public class CustomDynamicData : System.Dynamic.DynamicObject
public CustomDynamicData(string fix, Dictionary data)
{
Fix = fix;
- Dynamic = data;
+ Columns = data;
}
///
@@ -37,12 +37,6 @@ public class CustomDynamicData : System.Dynamic.DynamicObject
///
public CustomDynamicData() : this("", new()) { }
- ///
- public override IEnumerable GetDynamicMemberNames()
- {
- return Dynamic.Keys.Append(nameof(Fix));
- }
-
///
///
///
@@ -55,9 +49,9 @@ public class CustomDynamicData : System.Dynamic.DynamicObject
{
result = Fix;
}
- else if (Dynamic.ContainsKey(binder.Name))
+ else if (Columns.ContainsKey(binder.Name))
{
- result = Dynamic[binder.Name];
+ result = Columns[binder.Name];
}
else
{
@@ -82,9 +76,9 @@ public class CustomDynamicData : System.Dynamic.DynamicObject
Fix = v;
ret = true;
}
- else if (Dynamic.ContainsKey(binder.Name))
+ else if (Columns.ContainsKey(binder.Name))
{
- Dynamic[binder.Name] = v;
+ Columns[binder.Name] = v;
ret = true;
}
return ret;
diff --git a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor
index 17f30af9bcb6cec298013309d57cfaf741c4c30f..3b5adcc09bca10dfb7fe09027cb35e8f8da66340 100644
--- a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor
+++ b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor
@@ -11,20 +11,28 @@
@foreach (var element in DynamicColumnList)
{
-
+
+
+ Template - @v.Value
+
+
}
-
@foreach (var element in StaticColumnList)
{
-
+
+
+ Template - @v.Value
+
+
}
diff --git a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs
index 1a90032dd7646d859de0216c0a97c2e6994273a0..430cb736fbf0a995d22b4dffc0b88541a29b9fec 100644
--- a/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs
+++ b/src/BootstrapBlazor.Shared/Samples/Table/TablesDynamicObject.razor.cs
@@ -10,7 +10,7 @@ namespace BootstrapBlazor.Shared.Samples.Table;
public partial class TablesDynamicObject
{
[NotNull]
- private IEnumerable? BootstrapDynamicItems { get; set; }
+ private IEnumerable? CustomDynamicItems { get; set; }
private static List StaticColumnList => new() { "A", "B", "C", "Z" };
@@ -28,8 +28,8 @@ public partial class TablesDynamicObject
var now = DateTime.Now;
DynamicColumnList = Enumerable.Range(1, 5).Select(index => now.AddMinutes(-1 * index).ToString("HH:mm")).ToList();
- BootstrapDynamicItems = Enumerable.Range(1, 10)
- .Select(index => new BootstrapBlazorDynamicObjectData(index.ToString(), StaticColumnList.ToDictionary(d => d, d => $"{d}{index}")));
+ CustomDynamicItems = Enumerable.Range(1, 10)
+ .Select(index => new CustomDynamicColumnsObjectData(index.ToString(), StaticColumnList.ToDictionary(d => d, d => (object?)$"{d}{index}")));
}
private readonly static Random random = new();
diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj
index e449b8dd45e7065936286082d480ed8a5ae80b0a..b6ba1da869b6eb54fce1ddd342fd579128191997 100644
--- a/src/BootstrapBlazor/BootstrapBlazor.csproj
+++ b/src/BootstrapBlazor/BootstrapBlazor.csproj
@@ -1,7 +1,7 @@
- 6.11.15
+ 6.11.16
diff --git a/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs b/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs
index 729200d6b441b3e5f928a4f6ed2ef2402f6a658b..a418543c4c490a79bb0fb20277260ded800ce24c 100644
--- a/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs
+++ b/src/BootstrapBlazor/Components/Clipboard/Clipboard.cs
@@ -50,11 +50,12 @@ public class Clipboard : BootstrapModuleComponentBase
/// Dispose 方法
///
///
- protected virtual void Dispose(bool disposing)
+ protected override async ValueTask DisposeAsync(bool disposing)
{
if (disposing)
{
ClipboardService.UnRegister(this);
}
+ await base.DisposeAsync(disposing);
}
}
diff --git a/src/BootstrapBlazor/Dynamic/DynamicColumnsObject.cs b/src/BootstrapBlazor/Dynamic/DynamicColumnsObject.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1381bd088750555c7629367d9500a9572272d535
--- /dev/null
+++ b/src/BootstrapBlazor/Dynamic/DynamicColumnsObject.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+// Website: https://www.blazor.zone or https://argozhang.github.io/
+
+namespace BootstrapBlazor.Components;
+
+///
+/// 动态列类
+///
+public class DynamicColumnsObject : IDynamicColumnsObject
+{
+ ///
+ ///
+ ///
+ public Dictionary Columns { get; set; }
+
+ ///
+ /// 获得/设置 行主键
+ ///
+ public Guid DynamicObjectPrimaryKey { get; set; }
+
+ ///
+ /// 构造函数
+ ///
+ ///
+ public DynamicColumnsObject(Dictionary columnsData)
+ {
+ Columns = columnsData;
+ }
+
+ ///
+ /// 构造函数
+ ///
+ public DynamicColumnsObject() : this(new()) { }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual object? GetValue(string propertyName)
+ {
+ return Columns.TryGetValue(propertyName, out object? v) ? v : null;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual void SetValue(string propertyName, object? value)
+ {
+ Columns[propertyName] = value;
+ }
+}
diff --git a/src/BootstrapBlazor/Dynamic/IDynamicColumnsObject.cs b/src/BootstrapBlazor/Dynamic/IDynamicColumnsObject.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2b1872d92abdc1dd7c82020f1ab724c889dfb4ae
--- /dev/null
+++ b/src/BootstrapBlazor/Dynamic/IDynamicColumnsObject.cs
@@ -0,0 +1,16 @@
+// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+// Website: https://www.blazor.zone or https://argozhang.github.io/
+
+namespace BootstrapBlazor.Components;
+
+///
+/// 动态对象接口
+///
+public interface IDynamicColumnsObject : IDynamicObject
+{
+ ///
+ /// 获得设置 列与列数值集合
+ ///
+ public Dictionary Columns { get; set; }
+}
diff --git a/src/BootstrapBlazor/Services/CacheManager.cs b/src/BootstrapBlazor/Services/CacheManager.cs
index a851df8ab1ac09e4239af1f8cdccdcc1db94f828..821ca73398b74451a2ddda6c99710025a6c51b47 100644
--- a/src/BootstrapBlazor/Services/CacheManager.cs
+++ b/src/BootstrapBlazor/Services/CacheManager.cs
@@ -467,14 +467,22 @@ internal class CacheManager : ICacheManager
{
throw new ArgumentNullException(nameof(model));
}
- var type = model.GetType();
- var cacheKey = ($"Lambda-Get-{type.FullName}", typeof(TModel), fieldName, typeof(TResult));
- var invoker = Instance.GetOrCreate(cacheKey, entry =>
+
+ return (model is IDynamicColumnsObject d)
+ ? (TResult)d.GetValue(fieldName)!
+ : GetValue();
+
+ TResult GetValue()
{
- entry.SetDynamicAssemblyPolicy(type);
- return LambdaExtensions.GetPropertyValueLambda(model, fieldName).Compile();
- });
- return invoker(model);
+ var type = model.GetType();
+ var cacheKey = ($"Lambda-Get-{type.FullName}", typeof(TModel), fieldName, typeof(TResult));
+ var invoker = Instance.GetOrCreate(cacheKey, entry =>
+ {
+ entry.SetDynamicAssemblyPolicy(type);
+ return LambdaExtensions.GetPropertyValueLambda(model, fieldName).Compile();
+ });
+ return invoker(model);
+ }
}
public static void SetPropertyValue(TModel model, string fieldName, TValue value)
@@ -483,14 +491,27 @@ internal class CacheManager : ICacheManager
{
throw new ArgumentNullException(nameof(model));
}
- var type = model.GetType();
- var cacheKey = ($"Lambda-Set-{type.FullName}", typeof(TModel), fieldName, typeof(TValue));
- var invoker = Instance.GetOrCreate(cacheKey, entry =>
+
+ if (model is IDynamicColumnsObject d)
{
- entry.SetDynamicAssemblyPolicy(type);
- return LambdaExtensions.SetPropertyValueLambda(model, fieldName).Compile();
- });
- invoker(model, value);
+ d.SetValue(fieldName, value);
+ }
+ else
+ {
+ SetValue();
+ }
+
+ void SetValue()
+ {
+ var type = model.GetType();
+ var cacheKey = ($"Lambda-Set-{type.FullName}", typeof(TModel), fieldName, typeof(TValue));
+ var invoker = Instance.GetOrCreate(cacheKey, entry =>
+ {
+ entry.SetDynamicAssemblyPolicy(type);
+ return LambdaExtensions.SetPropertyValueLambda(model, fieldName).Compile();
+ });
+ invoker(model, value);
+ }
}
///
diff --git a/test/UnitTest/Dynamic/DynamicColumnsObjectTest.cs b/test/UnitTest/Dynamic/DynamicColumnsObjectTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2ae7815a90610e8cd409c1da6779f821bc3b0552
--- /dev/null
+++ b/test/UnitTest/Dynamic/DynamicColumnsObjectTest.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+// Website: https://www.blazor.zone or https://argozhang.github.io/
+
+namespace UnitTest.Dynamic;
+
+public class DynamicColumnsObjectTest
+{
+ [Fact]
+ public void DynamicColumnsObject_Ok()
+ {
+ var data = new DynamicColumnsObject();
+ Assert.Empty(data.Columns);
+ }
+
+ [Fact]
+ public void DynamicColumnsObject_Columns()
+ {
+ var data = new DynamicColumnsObject(new Dictionary()
+ {
+ ["A"] = "1",
+ ["B"] = 1
+ });
+
+ Assert.Equal("1", data.Columns["A"]);
+ Assert.Equal(1, data.Columns["B"]);
+ }
+
+ [Fact]
+ public void DynamicColumnsObject_Key()
+ {
+ var guid = Guid.NewGuid();
+ var data = new DynamicColumnsObject()
+ {
+ DynamicObjectPrimaryKey = guid
+ };
+
+ Assert.Equal(guid, data.DynamicObjectPrimaryKey);
+ }
+
+ [Fact]
+ public void DynamicColumnsObject_Value()
+ {
+ var data = new DynamicColumnsObject();
+ data.Columns["A"] = "1";
+ Assert.Equal("1", data.Columns["A"]);
+
+ data.SetValue("A", 2);
+ Assert.Equal(2, data.GetValue("A"));
+ Assert.Equal(2, data.Columns["A"]);
+
+ Assert.Null(data.GetValue("B"));
+ Assert.Throws(() => data.Columns["B"]);
+ }
+}
diff --git a/test/UnitTest/Services/ClipboardTest.cs b/test/UnitTest/Services/ClipboardTest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1fb00092a127d0ed572102301bca87a07a7756b9
--- /dev/null
+++ b/test/UnitTest/Services/ClipboardTest.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+// Website: https://www.blazor.zone or https://argozhang.github.io/
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace UnitTest.Services;
+
+public class ClipboardServiceTest : BootstrapBlazorTestBase
+{
+ [Fact]
+ public void ClipboardService_Null()
+ {
+ var service = Context.Services.GetRequiredService();
+ Assert.ThrowsAsync(() => service.Copy("test"));
+ }
+
+ [Fact]
+ public void ClipboardService_Ok()
+ {
+ var service = Context.Services.GetRequiredService();
+ var cut = Context.RenderComponent();
+ var copied = false;
+ service.Copy("Test", () =>
+ {
+ copied = true;
+ return Task.CompletedTask;
+ });
+ Assert.True(copied);
+
+ _ = cut.Instance.DisposeAsync();
+ }
+}