Blazor中如何实现支持行列展开的TextField表格
Blazor实现全TextField单元格、支持按钮触发行/列展开的表格方案
核心实现思路
不用引入第三方组件、不用写JS交互,原生Blazor的状态驱动机制就能实现,入门阶段越简单的逻辑越不容易出问题:所有展开收起的效果本质就是靠变量控制要不要多渲染一行/一列的DOM,所有单元格直接绑定输入框就行。
第一步:定义基础状态和数据模型
先把表格数据、展开状态存在组件的代码段里,不用搞复杂的实体类,新手用嵌套列表存每个输入框的值最直观:
@code { // 记录当前展开的行/列索引,-1代表没有展开内容 private int expandedRowIndex = -1; private int expandedColumnIndex = -1; // 表格数据:外层List是行,内层List是每行对应列的输入框值 private List<List<string>> TableData { get; set; } = new(); // 初始基础行列数 private int baseRowCount = 3; private int baseColCount = 3; protected override void OnInitialized() { // 初始化默认3*3的空表格 for (int i = 0; i < baseRowCount; i++) { var row = new List<string>(); for (int j = 0; j < baseColCount; j++) { row.Add(string.Empty); } TableData.Add(row); } } }
第二步:写展开/收起的触发逻辑
按钮点击的时候直接改状态变量就行,Blazor会自动重新渲染UI,不用手动刷新页面:
@code { // 触发行展开/收起 private void ToggleRowExpand(int rowIndex) { // 点当前已展开的行就收起,点其他行就切换展开目标 expandedRowIndex = expandedRowIndex == rowIndex ? -1 : rowIndex; // 给展开的行补全对应列数的空值,避免索引越界 if (expandedRowIndex != -1) { while (TableData[expandedRowIndex].Count < GetCurrentColCount()) { TableData[expandedRowIndex].Add(string.Empty); } } } // 触发列展开/收起 private void ToggleColumnExpand(int colIndex) { expandedColumnIndex = expandedColumnIndex == colIndex ? -1 : colIndex; // 给所有行补全新增列的空值 if (expandedColumnIndex != -1) { foreach (var row in TableData) { while (row.Count < GetCurrentColCount()) { row.Add(string.Empty); } } } } // 计算当前实际要渲染的总列数 private int GetCurrentColCount() { return baseColCount + (expandedColumnIndex == -1 ? 0 : 1); } // 计算当前实际要渲染的总行数 private int GetCurrentRowCount() { return baseRowCount + (expandedRowIndex == -1 ? 0 : 1); } }
第三步:写Razor渲染模板,所有单元格用文本输入框
直接循环渲染行列,每个单元格放input做文本输入,对应位置插展开/收起按钮:
<table border="1" cellpadding="8" cellspacing="0" style="border-collapse:collapse"> <thead> <tr> <th>操作</th> @* 渲染基础列的表头+列展开按钮 *@ @for (int j = 0; j < baseColCount; j++) { <th> 列@(j+1) <button @onclick="() => ToggleColumnExpand(j)" style="margin-left:6px;font-size:12px"> @(expandedColumnIndex == j ? "收起列" : "展开列") </button> </th> } @* 有展开列的时候渲染新增列表头 *@ @if (expandedColumnIndex != -1) { <th>新增列</th> } </tr> </thead> <tbody> @for (int i = 0; i < GetCurrentRowCount(); i++) { <tr> @* 行首的操作列:基础行显示展开按钮,展开的新增行显示标识 *@ @if (i < baseRowCount) { <td> <button @onclick="() => ToggleRowExpand(i)"> @(expandedRowIndex == i ? "收起行" : "展开行") </button> </td> } else { <td>新增行</td> } @* 渲染当前行所有单元格,全是文本输入框 *@ @for (int j = 0; j < GetCurrentColCount(); j++) { <td> @* 边界补全,避免索引越界报错 *@ if (i >= TableData.Count) { TableData.Add(Enumerable.Repeat(string.Empty, GetCurrentColCount()).ToList()); } if (j >= TableData[i].Count) { TableData[i].Add(string.Empty); } <input type="text" @bind="TableData[i][j]" style="width:120px" /> </td> } </tr> } </tbody> </table>
新手避坑提醒
- 别上来就套第三方表格组件,先把上面的原生逻辑跑通,不然出了bug你都分不清是自己写错了还是组件封装的问题,白耗时间。
- 循环绑按钮事件的时候注意别踩闭包坑,上面代码里的lambda传参方式直接用for循环的i/j变量就行,别乱把变量提到循环外,容易出现点哪个按钮都触发最后一行/列逻辑的问题。
- 完全不用写JS操作DOM,Blazor会自动对比状态变化更新UI,手动插DOM反而容易和Blazor的渲染逻辑冲突。
- 要是嫌Blazor自带的
InputText组件必须套EditForm麻烦,直接用原生<input type="text">绑定值就行,和你要的TextField效果完全一致,新手用这个省很多没必要的配置。 - 后续如果要支持同时展开多行、多列,把现在存单个索引的int变量换成
HashSet<int>存所有展开的行/列索引就行,核心逻辑不用动,改下展开判断的条件就成。
内容的提问来源于stack exchange,提问作者Ztaft
相关产品推荐
相关产品推荐

