You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在ASP.NET及ASP.NET Core中从模型类动态生成HTML表格?

如何在ASP.NET及ASP.NET Core中从模型类动态生成HTML表格?

嘿,很高兴能帮你解决这个从UnitPrice模型生成HTML表格的问题!不管你用的是传统ASP.NET(Web Forms)还是ASP.NET Core,都有灵活的动态实现方案,我结合你的模型给你拆解一下:

核心思路

动态生成的关键是利用反射获取模型的属性信息,同时读取DisplayNameReadOnly这类数据注解,这样既能自动生成友好的表头,还能识别只读字段做特殊处理,不用硬编码每个列。


1. 传统ASP.NET(Web Forms)的实现方式

方式一:后台生成HTML字符串(灵活可控)

你可以写一个通用方法,传入UnitPrice的集合,直接生成完整的HTML表格:

using System.Text;
using System.Reflection;
using System.ComponentModel;

public string GenerateHtmlTable<T>(IEnumerable<T> data)
{
    var sb = new StringBuilder();
    var type = typeof(T);
    var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);

    // 构建表格框架和表头
    sb.Append("<table border='1' cellpadding='4' cellspacing='0'>");
    sb.Append("<thead><tr style='background-color: #f0f0f0;'>");
    
    foreach (var prop in properties)
    {
        // 优先用DisplayName,没有则用属性名
        var headerText = prop.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? prop.Name;
        // 给只读字段的表头加样式标记
        var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
        var headerStyle = isReadOnly ? " style='color: #666;'" : "";
        
        sb.Append($"<th{headerStyle}>{headerText}</th>");
    }
    
    sb.Append("</tr></thead><tbody>");

    // 构建数据行
    foreach (var item in data)
    {
        sb.Append("<tr>");
        foreach (var prop in properties)
        {
            var cellValue = prop.GetValue(item)?.ToString() ?? "-";
            var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
            var cellStyle = isReadOnly ? " style='color: #666;'" : "";
            
            sb.Append($"<td{cellStyle}>{cellValue}</td>");
        }
        sb.Append("</tr>");
    }

    sb.Append("</tbody></table>");
    return sb.ToString();
}

然后在ASPX页面里直接调用:

<%-- 假设UnitPriceList是你从数据库获取的List<UnitPrice> --%>
<%= GenerateHtmlTable(UnitPriceList) %>

方式二:用GridView自动生成(快速便捷)

如果不想自己写HTML,ASP.NET的GridView控件可以直接绑定数据源,自动识别数据注解:

<asp:GridView ID="UnitPriceGridView" runat="server" 
    AutoGenerateColumns="True" 
    DataKeyNames="IdProduct,IdUnit"
    CssClass="table table-striped"
    EmptyDataText="暂无数据">
</asp:GridView>

后台绑定数据:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        // 替换成你的数据源获取逻辑
        var unitPrices = GetUnitPricesFromDatabase();
        UnitPriceGridView.DataSource = unitPrices;
        UnitPriceGridView.DataBind();
    }
}

GridView会自动把DisplayName作为列标题,ReadOnly属性也会让对应单元格不可编辑(如果开启编辑模式的话)。


2. ASP.NET Core的实现方式

ASP.NET Core更推荐用Razor视图或自定义辅助工具来实现,这里给你三种常用方案:

方式一:Razor视图直接反射生成(快速实现)

直接在.cshtml视图里写逻辑,适合简单场景:

@model IEnumerable<UnitPrice>
@using System.Reflection
@using System.ComponentModel

<table class="table table-bordered table-striped">
    <thead class="table-light">
        <tr>
            @foreach (var prop in typeof(UnitPrice).GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var headerText = prop.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? prop.Name;
                var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
                <th class="@(isReadOnly ? "text-muted" : "")">@headerText</th>
            }
        </tr>
    </thead>
    <tbody>
        @if (!Model.Any())
        {
            <tr>
                <td colspan="@typeof(UnitPrice).GetProperties().Length" class="text-center text-muted">暂无数据</td>
            </tr>
        }
        else
        {
            @foreach (var item in Model)
            {
                <tr>
                    @foreach (var prop in typeof(UnitPrice).GetProperties(BindingFlags.Public | BindingFlags.Instance))
                    {
                        var cellValue = prop.GetValue(item)?.ToString() ?? "-";
                        var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
                        <td class="@(isReadOnly ? "text-muted" : "")">@Html.Raw(Html.Encode(cellValue))</td>
                    }
                </tr>
            }
        }
    </tbody>
</table>

方式二:自定义HTML Helper(复用性强)

如果要在多个视图里复用,可以写一个通用的HTML Helper:

  1. 先创建Helpers/HtmlTableHelper.cs
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Reflection;
using System.ComponentModel;

namespace YourProjectNamespace.Helpers
{
    public static class HtmlTableHelper
    {
        public static IHtmlContent GenerateDynamicTable<T>(this IHtmlHelper htmlHelper, IEnumerable<T> data)
        {
            var type = typeof(T);
            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            var table = new TagBuilder("table");
            table.AddCssClass("table table-bordered");

            // 生成表头
            var thead = new TagBuilder("thead");
            var headerRow = new TagBuilder("tr");
            headerRow.AddCssClass("table-light");

            foreach (var prop in properties)
            {
                var headerText = prop.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? prop.Name;
                var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
                var th = new TagBuilder("th");
                
                if (isReadOnly) th.AddCssClass("text-muted");
                th.InnerHtml.Append(headerText);
                headerRow.InnerHtml.AppendHtml(th);
            }

            thead.InnerHtml.AppendHtml(headerRow);
            table.InnerHtml.AppendHtml(thead);

            // 生成数据行
            var tbody = new TagBuilder("tbody");
            if (!data.Any())
            {
                var emptyRow = new TagBuilder("tr");
                var emptyCell = new TagBuilder("td");
                emptyCell.Attributes["colspan"] = properties.Length.ToString();
                emptyCell.AddCssClass("text-center text-muted");
                emptyCell.InnerHtml.Append("暂无数据");
                emptyRow.InnerHtml.AppendHtml(emptyCell);
                tbody.InnerHtml.AppendHtml(emptyRow);
            }
            else
            {
                foreach (var item in data)
                {
                    var row = new TagBuilder("tr");
                    foreach (var prop in properties)
                    {
                        var cellValue = prop.GetValue(item)?.ToString() ?? "-";
                        var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
                        var td = new TagBuilder("td");
                        
                        if (isReadOnly) td.AddCssClass("text-muted");
                        td.InnerHtml.Append(htmlHelper.Encode(cellValue));
                        row.InnerHtml.AppendHtml(td);
                    }
                    tbody.InnerHtml.AppendHtml(row);
                }
            }

            table.InnerHtml.AppendHtml(tbody);
            return table;
        }
    }
}
  1. _ViewImports.cshtml里注册Helper:
@using YourProjectNamespace.Helpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
  1. 然后在视图里直接调用:
@model IEnumerable<UnitPrice>

@Html.GenerateDynamicTable(Model)

方式三:自定义Tag Helper(Core风格)

Tag Helper是Core的特色,可以像HTML标签一样使用,更直观:

  1. 创建TagHelpers/DynamicTableTagHelper.cs
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Reflection;
using System.ComponentModel;

namespace YourProjectNamespace.TagHelpers
{
    [HtmlTargetElement("dynamic-table", Attributes = "model")]
    public class DynamicTableTagHelper : TagHelper
    {
        [HtmlAttributeName("model")]
        public IEnumerable<object> Model { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "table";
            output.AddClass("table table-bordered", HtmlEncoder.Default);

            if (Model == null || !Model.Any())
            {
                output.Content.SetHtmlContent("<tr><td colspan='100%' class='text-center text-muted'>暂无数据</td></tr>");
                return;
            }

            var itemType = Model.First().GetType();
            var properties = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

            // 生成表头
            var headerRow = new TagBuilder("tr");
            headerRow.AddCssClass("table-light");
            
            foreach (var prop in properties)
            {
                var headerText = prop.GetCustomAttribute<DisplayNameAttribute>()?.DisplayName ?? prop.Name;
                var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
                var th = new TagBuilder("th");
                
                if (isReadOnly) th.AddCssClass("text-muted");
                th.InnerHtml.Append(headerText);
                headerRow.InnerHtml.AppendHtml(th);
            }

            var thead = new TagBuilder("thead");
            thead.InnerHtml.AppendHtml(headerRow);
            output.Content.AppendHtml(thead);

            // 生成数据行
            var tbody = new TagBuilder("tbody");
            foreach (var item in Model)
            {
                var row = new TagBuilder("tr");
                foreach (var prop in properties)
                {
                    var cellValue = prop.GetValue(item)?.ToString() ?? "-";
                    var isReadOnly = prop.GetCustomAttribute<ReadOnlyAttribute>()?.IsReadOnly ?? false;
                    var td = new TagBuilder("td");
                    
                    if (isReadOnly) td.AddCssClass("text-muted");
                    td.InnerHtml.Append(HtmlEncoder.Default.Encode(cellValue));
                    row.InnerHtml.AppendHtml(td);
                }
                tbody.InnerHtml.AppendHtml(row);
            }

            output.Content.AppendHtml(tbody);
        }
    }
}
  1. _ViewImports.cshtml注册Tag Helper:
@using YourProjectNamespace.TagHelpers
@addTagHelper *, YourProjectNamespace
  1. 视图里使用:
@model IEnumerable<UnitPrice>

<dynamic-table model="@Model"></dynamic-table>

注意事项

  • 性能优化:如果要处理大量数据,建议缓存模型的属性信息(比如用MemoryCache),避免每次反射消耗资源。
  • 类型格式化:对于DateTimedecimal这类特殊类型,可以在反射时判断类型,做格式化处理(比如价格保留两位小数)。
  • XSS防护:输出数据时一定要用HtmlEncoder编码,避免跨站脚本攻击,上面的例子里已经做了处理。

内容的提问来源于stack exchange,提问作者Propaanbanaan

火山引擎 最新活动