在ASP.NET中,ListView控件是一个灵活的数据展示组件,它允许开发者自定义数据的展示方式。其中一种高级用法是动态生成列,这可以通过配置AutoGenerateColumns属性来实现。本文将介绍如何使用Common Table Expressions(CTE)和PIVOT操作来创建一个Employee-vs-Vendor表,并在ListView中动态生成列。
首先,需要创建一个Employee-vs-Vendor表,这可以通过使用CTE和PIVOT来实现。具体的SQL查询语句和数据表结构可以在附带的源代码中找到。
在ListView的布局模板中,需要声明一个占位符,用于后续动态生成列。以下是HTML代码示例:
<LayoutTemplate>
<div class="listviewGrid">
<table id="tblViewHdr" class="CollapsableExpandable" cellpadding="0" cellspacing="0" style="word-break:break-all;word-wrap:break-word">
<tr class="head">
<asp:PlaceHolder runat="server" ID="phDynamicHdr" />
</tr>
<tr id="itemPlaceholder" runat="server" />
</table>
</div>
</LayoutTemplate>
CSS样式用于处理列过多时的换行问题,确保内容不会溢出表格。
在ItemTemplate中,不需要额外添加动态列,因为这些列将通过程序自动生成。以下是HTML代码示例:
<ItemTemplate>
<tr id="row" runat="server" class="item">
<td class="first">
<img src="Images/minus.png" />
</td>
</tr>
</ItemTemplate>
根据查询的级别,可能需要创建嵌套的ItemTemplate。这可以通过AJAX来实现,但本文暂不涉及这部分内容。
当开始将查询结果绑定到ListView时,需要从CTE和PIVOT获取的SQL结果逐级绑定。以下是C#代码示例:
public void bindListView(DataTable dtEmpID)
{
DataTable dt = new DataTable();
dt = ((DataTable)ViewState["cachedTable"]).Clone();
if (((DataTable)ViewState["cachedTable"]).Rows.Count > 0)
{
DataRow[] drResults = ((DataTable)ViewState["cachedTable"]).Select("GENERATION = 0");
foreach (DataRow dr in drResults)
{
object[] row = dr.ItemArray;
dt.Rows.Add(row);
}
}
lsvHighLvlFormAccess.DataSource = dt;
lsvHighLvlFormAccess.DataBind();
}
在最高级别的绑定完成后,可以填充表头标题。这是设置列跨度和动态生成列标题的部分。
以下是C#代码示例,展示了如何在DataBound事件中动态生成列标题:
protected void lsvHighLvlFormAccess_DataBound(object sender, EventArgs e)
{
PlaceHolder phDynamicHdr = (PlaceHolder)lsvHighLvlFormAccess.FindControl("phDynamicHdr");
if (phDynamicHdr != null)
{
Literal ltrl = new Literal();
DataTable dt = ((DataTable)ViewState["cachedTable"]);
if (dt != null && dt.Rows.Count > 0)
{
foreach (DataColumn dc in dt.Rows[0].Table.Columns)
{
if (!(dc.ColumnName.Equals("GENERATION") || dc.ColumnName.Equals("hierarchy") || dc.ColumnName.Equals("rowNo") || dc.ColumnName.Equals("EmployeeID")))
{
if (dc.ColumnName.Equals("LoginID"))
{
ltrl.Text += "" + dc.ColumnName + " ";
}
else
ltrl.Text += "" + dc.ColumnName + " ";
}
}
}
if (phDynamicHdr.Controls.Count > 0)
{
if (!((Literal)phDynamicHdr.Controls[0]).Text.Equals(ltrl.Text))
{
phDynamicHdr.Controls.Remove(phDynamicHdr.Controls[0]);
phDynamicHdr.Controls.Add(ltrl);
}
}
else
phDynamicHdr.Controls.Add(ltrl);
}
HtmlTableCell td = (HtmlTableCell)lsvHighLvlFormAccess.FindControl("imgCollapseExpand");
if (td != null)
{
DataTable dt = ((DataTable)ViewState["cachedTable"]);
if (dt != null && dt.Rows.Count > 0)
{
td.ColSpan = dt.Rows[0].Table.Columns.Count + I_COLSPAN - 1;
}
}
}
在每一级的行绑定时,需要触发事件以填充其后代。这里使用了复选框,但也可以使用其他控件。
以下是C#代码示例,展示了如何在ItemDataBound事件中动态填充行:
protected void lsvHighLvlFormAccess_ItemDataBound(object sender, ListViewItemEventArgs e)
{
HtmlTableRow row = (HtmlTableRow)e.Item.FindControl("row");
ListViewDataItem item = (ListViewDataItem)e.Item;
System.Data.DataRowView drv = (System.Data.DataRowView)item.DataItem;
dynamicPopulateRow(row, drv, 0);
}
其中dynamicPopulateRow()方法如下:
private void dynamicPopulateRow(HtmlTableRow row, System.Data.DataRowView drv, int iGeneration)
{
if (row != null)
{
foreach (DataColumn dc in drv.Row.Table.Columns)
{
string sEmployeeID = drv["LoginID"].ToString();
if (dc.ColumnName.Equals("LoginID"))
{
HtmlTableCell cell = new HtmlTableCell("td");
cell.Controls.Add(new LiteralControl(Convert.ToString(drv[dc.ColumnName])));
cell.ColSpan = dc.ColumnName.Equals("LoginID") ? I_COLSPAN - iGeneration : 1;
row.Cells.Add(cell);
}
else if (!(dc.ColumnName.Equals("GENERATION") || dc.ColumnName.Equals("hierarchy") || dc.ColumnName.Equals("rowNo") || dc.ColumnName.Equals("EmployeeID")))
{
HtmlTableCell cell = new HtmlTableCell("td");
bool bIsNull = drv[dc.ColumnName] is System.DBNull;
Literal ltrl = new Literal();
ltrl.Text += " 0 ? "checked>" : ">");
cell.Controls.Add(ltrl);
row.Cells.Add(cell);
}
else
{
// 其他行
}
}
}
}
如果有更多的嵌套级别,只需在ItemDataBound事件的末尾添加这些代码:
var lst1stLevel = (ListView)e.Item.FindControl("lst1stLevel");
populateLV(lst1stLevel, 1, (string)drv["hierarchy"], Convert.ToInt32(drv["rowNo"]));
其中populateLV()方法如下:
private void populateLV(ListView lv, int iNextGeneration, string sHierarchy, int iCurrRowNo)
{
if (lv != null)
{
DataTable dt = new DataTable();
dt = ((DataTable)ViewState["cachedTable"]).Clone();
List levels = ((DataTable)ViewState["cachedTable"]).Select("GENERATION = " + (iNextGeneration > 0 ? iNextGeneration - 1 : 0) + " AND hierarchy LIKE '" + sHierarchy + "%'").AsEnumerable().Select(al => Convert.ToInt32(al.Field