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
39 changes: 30 additions & 9 deletions src/BootstrapBlazor/Extensions/PropertyInfoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ namespace BootstrapBlazor.Components;

/// <summary>
/// <para lang="zh">PropertyInfo 扩展方法</para>
/// <para lang="en">PropertyInfo 扩展方法</para>
/// <para lang="en">PropertyInfo extension methods</para>
/// </summary>
public static class PropertyInfoExtensions
{
/// <summary>
/// <para lang="zh">判断属性是否为静态属性</para>
/// <para lang="en">判断propertywhether为静态property</para>
/// <para lang="en">Determines whether the property is static</para>
/// </summary>
/// <param name="p"></param>
public static bool IsStatic(this PropertyInfo p)
Expand All @@ -25,17 +25,12 @@ public static bool IsStatic(this PropertyInfo p)
}

/// <summary>
/// <para lang="zh">判断属性是否只读扩展方法</para>
/// <para lang="en">判断propertywhether只读扩展方法</para>
/// <para lang="zh">判断属性是否可以写入扩展方法</para>
/// <para lang="en">Determines whether the property can be written to extension method</para>
/// </summary>
/// <param name="p"></param>
public static bool IsCanWrite(this PropertyInfo p) => p.CanWrite && !p.IsInit();

/// <summary>
/// <para lang="zh">判断是否为 Init 扩展方法</para>
/// <para lang="en">判断whether为 Init 扩展方法</para>
/// </summary>
/// <param name="p"></param>
private static bool IsInit(this PropertyInfo p)
{
var isInit = false;
Expand All @@ -50,4 +45,30 @@ private static bool IsInit(this PropertyInfo p)
}
return isInit;
}

/// <summary>
/// <para lang="zh">判断属性是否有指定类型的 Parameter 特性</para>
/// <para lang="en">Determines whether the property has a Parameter attribute of the specified type</para>
/// </summary>
/// <param name="modelProperty"></param>
/// <param name="type"></param>
public static bool HasParameterAttribute(this PropertyInfo? modelProperty, Type type)
{
if (modelProperty is null)
{
return false;
}

// 必须带有 Parameter 特性
if (!modelProperty.IsDefined(typeof(ParameterAttribute), inherit: true))
{
return false;
}

// 处理可空类型,并使用可赋值性检查类型兼容性
var propertyType = Nullable.GetUnderlyingType(modelProperty.PropertyType) ?? modelProperty.PropertyType;
var targetType = Nullable.GetUnderlyingType(type) ?? type;

return targetType.IsAssignableFrom(propertyType);
}
}
191 changes: 191 additions & 0 deletions src/BootstrapBlazor/Misc/EditorItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// 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;

/// <summary>
/// <para lang="zh">EditorItem 表单渲染项实体类</para>
/// <para lang="en">EditorItem form field class</para>
/// </summary>
/// <param name="fieldName"></param>
/// <param name="fieldType"></param>
/// <param name="fieldText"></param>
public class EditorItem<TModel>(string fieldName, Type fieldType, string? fieldText = null) : IEditorItem
{
private string FieldName { get; } = fieldName;

/// <summary>
/// <inheritdoc cref="IEditorItem.SkipValidate"/>
/// </summary>
public bool SkipValidate { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Ignore"/>
/// </summary>
public bool? Ignore { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Readonly"/>
/// </summary>
public bool? Readonly { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Required"/>
/// </summary>
public bool? Required { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.RequiredErrorMessage"/>
/// </summary>
public string? RequiredErrorMessage { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.ShowLabelTooltip"/>
/// </summary>
public bool? ShowLabelTooltip { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.PlaceHolder"/>
/// </summary>
public string? PlaceHolder { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.PropertyType"/>
/// </summary>
public Type PropertyType { get; } = fieldType;

[Obsolete("已弃用,请删除;Deprecated, please delete")]
[ExcludeFromCodeCoverage]
bool IEditorItem.Editable { get; set; } = true;

/// <summary>
/// <inheritdoc cref="IEditorItem.Step"/>
/// </summary>
public string? Step { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Rows"/>
/// </summary>
public int Rows { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Cols"/>
/// </summary>
public int Cols { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Text"/>
/// </summary>
[NotNull]
public string? Text { get; set; } = fieldText;

/// <summary>
/// <inheritdoc cref="IEditorItem.EditTemplate"/>
/// </summary>
public RenderFragment<TModel>? EditTemplate { get; set; }

RenderFragment<object>? IEditorItem.EditTemplate
{
get
{
return EditTemplate == null ? null : new RenderFragment<object>(item => builder =>
{
builder.AddContent(0, EditTemplate((TModel)item));
});
}
set
{
}
}

/// <summary>
/// <inheritdoc cref="IEditorItem.ComponentType"/>
/// </summary>
public Type? ComponentType { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.ComponentParameters"/>
/// </summary>
public IEnumerable<KeyValuePair<string, object>>? ComponentParameters { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Items"/>
/// </summary>
public IEnumerable<SelectedItem>? Items { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.Order"/>
/// </summary>
public int Order { get; set; }

/// <summary>
/// <inheritdoc cref="ILookup.Lookup"/>
/// </summary>
public IEnumerable<SelectedItem>? Lookup { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.ShowSearchWhenSelect"/>
/// </summary>
public bool ShowSearchWhenSelect { get; set; }

[Obsolete("已弃用,请删除;Deprecated, please delete")]
[ExcludeFromCodeCoverage]
bool IEditorItem.IsFixedSearchWhenSelect { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.IsPopover"/>
/// </summary>
public bool IsPopover { get; set; }

/// <summary>
/// <inheritdoc cref="ILookup.LookupStringComparison"/>
/// </summary>
public StringComparison LookupStringComparison { get; set; } = StringComparison.OrdinalIgnoreCase;

/// <summary>
/// <inheritdoc cref="ILookup.LookupServiceKey"/>
/// </summary>
public string? LookupServiceKey { get; set; }

/// <summary>
/// <inheritdoc cref="ILookup.LookupServiceData"/>
/// </summary>
public object? LookupServiceData { get; set; }

/// <summary>
/// <inheritdoc cref="ILookup.LookupService"/>
/// </summary>
public ILookupService? LookupService { get; set; }

/// <summary>
/// <inheritdoc cref="ITableColumn.OnCellRender"/>
/// </summary>
public Action<TableCellArgs>? OnCellRender { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.ValidateRules"/>
/// </summary>
public List<IValidator>? ValidateRules { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.GroupName"/>
/// </summary>
public string? GroupName { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.GroupOrder"/>
/// </summary>
public int GroupOrder { get; set; }

/// <summary>
/// <inheritdoc cref="IEditorItem.GetDisplayName"/>
/// </summary>
public string GetDisplayName() => Text;

/// <summary>
/// <inheritdoc cref="IEditorItem.GetFieldName"/>
/// </summary>
public string GetFieldName() => FieldName;
}
24 changes: 24 additions & 0 deletions src/BootstrapBlazor/Utils/BootstrapDynamicComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,28 @@ public RenderFragment Render() => builder =>
}
builder.CloseComponent();
};

/// <summary>
/// <para lang="zh">创建组件实例并渲染 Model 参数</para>
/// <para lang="en">Create component instance and render</para>
/// </summary>
public RenderFragment<TModel> RenderEditTemplate<TModel>(string? modelParameterName = null) => model => builder =>
{
var index = 0;
builder.OpenComponent(index++, componentType);
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);
}
}
builder.CloseComponent();
};
}
11 changes: 11 additions & 0 deletions test/UnitTest/Extensions/ObjectExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,17 @@ public void IsStatic_Ok()
Assert.True(pi.IsStatic());
}

[Fact]
public void HasParameterAttribute_Ok()
{
var instance = new MockObject();
var pi = instance.GetType().GetProperty("Mock");
Assert.False(pi.HasParameterAttribute(typeof(Foo)));

pi = instance.GetType().GetProperty(nameof(instance.Foo));
Assert.False(pi.HasParameterAttribute(typeof(Foo)));
}

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