我有两个存储过程,它们返回两组相关数据。数据是这样的。 第一个过程返回这样的数据
ISSUE_ID ISSUETYPE
-------------------------------------
1 ISSUE 1 TYPE
2 ISSUE 2 TYPE
3 ISSUE 3 TYPE
4 ISSUE 4 TYPE
第二个过程根据ISSUE_ID
返回这样的数据HEADER ID HEADER NAME ISSUE_ID
-----------------------------------------------------
1 HEADER 1 NAME 1
2 HEADER 2 NAME 1
3 HEADER 3 NAME 2
4 HEADER 4 NAME 2
5 HEADER 5 NAME 3
事情是如何基于ISSUE_ID
对此进行分组,并使用两个存储过程在gridview中将其显示在组中。我在很多论坛上搜索过,我发现选项是嵌套的gridview。我可以在不使用此嵌套gridview的情况下实现此目的。
最后我想在gridview中显示这样的内容。
ISSUE 1 TYPE
-----------------------------
HEADER 1 NAME
HEADER 2 NAME
ISSUE 2 TYPE
-----------------------------
HEADER 3 NAME
HEADER 4 NAME
ISSUE 3 TYPE
-----------------------------
HEADER 5 NAME
提前感谢百万..需要一些建议来实现这个目标。
答案 0 :(得分:6)
ASP.Net GridView中的分组示例
<asp:GridView ID="grdViewOrders" CssClass="serh-grid" runat="server" AutoGenerateColumns="False"
TabIndex="1" Width="100%" CellPadding="4" ForeColor="Black" GridLines="Vertical"
BackColor="White" BorderColor="#DEDFDE" BorderStyle="None" BorderWidth="1px"
OnRowDataBound="grdViewOrders_RowDataBound" OnRowCommand="grdViewOrders_RowCommand"
OnRowCreated="grdViewOrders_RowCreated">
<Columns>
<asp:BoundField DataField="OrderID" HeaderText="OrderID" SortExpression="OrderID" />
<asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
<asp:BoundField DataField="UnitPrice" HeaderText="UnitPrice" SortExpression="UnitPrice" />
<asp:BoundField DataField="Quantity" HeaderText="Quantity" SortExpression="Quantity" />
<asp:BoundField DataField="Discount" HeaderText="Discount" SortExpression="Discount" />
<asp:BoundField DataField="Amount" HeaderText="Amount" SortExpression="Amount" />
</Columns>
<FooterStyle BackColor="#CCCC99" />
<SelectedRowStyle CssClass="grid-sltrow" />
<HeaderStyle BackColor="#6B696B" Font-Bold="True" ForeColor="White" BorderStyle="Solid" BorderWidth="1px" BorderColor="Black" />
</asp:GridView>
备注:强>
主要逻辑位于GridView的RowCreated和RowDataBound事件中。
迭代我所有的行
在迭代结果集时,主索引的每一点都会发生变化:
标题显示为GridView中的新行。
GridView助手
使用GridViewHelper
下面我们将看到一些GridViewHelper示例。首先,我们显示将创建组和摘要的网格。示例数据来自Northwind数据库,只做了一些修改:
要为ItemTotal列创建摘要,我们只需要承诺的2行代码:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterSummary("ItemTotal", SummaryOperation.Sum);
}
首先,我们创建GridViewHelper,设置它将在构造函数中运行的网格。然后我们注册指定要执行的列名和摘要操作的摘要。结果如下:
在此示例中,添加了一个新行以显示摘要。另一个选项是使用页脚行显示摘要,而不是创建新摘要。将新行添加到网格时,仅创建显示汇总列的必需单元格。使用页脚,创建所有单元格。在组摘要的情况下,生成所有单元格或仅生成所需单元格是组属性。
现在我们将创建一个组。代码如下所示:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterGroup("ShipRegion", true, true);
helper.ApplyGroupSort();
}
RegisterGroup方法的第一个参数定义了必须为其创建组的列。它也可以创建一个由列数组组成的复合组。第二个参数指定组是否是自动的。在这种情况下,将为组头自动创建一个新行。第三个参数指定是否必须隐藏组列。 ApplyGroupSort方法将网格的排序表达式设置为组列,在本例中为ShipRegion。这是正确分组工作所必需的,除非数据是从数据库中订购的。
在上面的示例中,ShipRegion列已被隐藏:
让我们做一些更有趣的事情,让我们为创建的群组添加摘要。我们还需要一行来将摘要注册到小组:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterGroup("ShipRegion", true, true);
helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipRegion");
helper.ApplyGroupSort();
}
这次,RegisterSummary方法需要另一个参数。该参数指定必须为其创建摘要的组的名称。组名称是从组列名称自动生成的。如果该组只有一列,则组名称将是该列的名称。如果组具有多个列,则组名称将是组成该组的列的有序串联,并加上加号(&#34; +&#34;):&#34; ShipRegion + ShipName&# 34。
我们可以在网格下方看到分组和该组的摘要:
可以在网格中创建多个组,模拟分层分组,如下所示:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterGroup("ShipRegion", true, true);
helper.RegisterGroup("ShipName", true, true);
helper.ApplyGroupSort();
}
结果:
当有多个组时,可视化会受到影响。 GridViewHelper具有允许轻松实现视觉或功能调整的事件。事件列表如下:
通过更多代码行,我们可以改善网格的可视化方面:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterGroup("ShipRegion", true, true);
helper.RegisterGroup("ShipName", true, true);
helper.GroupHeader += new GroupEvent(helper_GroupHeader);
helper.ApplyGroupSort();
}
private void helper_GroupHeader(string groupName, object[] values, GridViewRow row)
{
if ( groupName == "ShipRegion" )
{
row.BackColor = Color.LightGray;
row.Cells[0].Text = " " + row.Cells[0].Text;
}
else if (groupName == "ShipName")
{
row.BackColor = Color.FromArgb(236, 236, 236);
row.Cells[0].Text = " " + row.Cells[0].Text;
}
}
化妆品后的网格:
更多分组选项
还有两个有趣的样本。第一个是复合组。第二个定义了一个抑制组,它具有与sql GROUP BY子句相同的行为。禁止重复值,并对其他列执行摘要操作。
下面我们可以看到复合组的代码和网格外观:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
string[] cols = new string[2];
cols[0] = "ShipRegion";
cols[1] = "ShipName";
helper.RegisterGroup(cols, true, true);
helper.ApplyGroupSort();
}
我们可以向小组添加摘要。这次我们将定义一个平均操作并添加一个标签来指示操作:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
string[] cols = new string[2];
cols[0] = "ShipRegion";
cols[1] = "ShipName";
helper.RegisterGroup(cols, true, true);
helper.RegisterSummary("ItemTotal", SummaryOperation.Avg, "ShipRegion+ShipName");
helper.GroupSummary += new GroupEvent(helper_GroupSummary);
helper.ApplyGroupSort();
}
private void helper_GroupSummary(string groupName, object[] values, GridViewRow row)
{
row.Cells[0].HorizontalAlign = HorizontalAlign.Right;
row.Cells[0].Text = "Average";
}
最后一个样本将创建一个抑制组。值得一提的是,如果定义了抑制组,则不能创建其他组。同样,如果已经定义了一个组,我们就无法创建一个抑制组,如果我们尝试它就会引发异常。
下面我们可以看到抑制组的代码和网格外观:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.SetSuppressGroup("ShipName");
helper.RegisterSummary("Quantity", SummaryOperation.Sum, "ShipName");
helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipName");
helper.ApplyGroupSort();
}
没有为未定义摘要操作的列显示值。这是有道理的,因为GridViewHelper不知道如何继续将组行中找到的值汇总为唯一值。这提醒了某些已知的信息:
&#34; Column&#39; column_name&#39;在选择列表中无效,因为它不包含在聚合函数或GROUP BY子句中。&#34;
显示没有摘要操作的列没有意义,要隐藏它们我们需要调用方法:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.SetSuppressGroup(rdBtnLstGroup.SelectedValue);
helper.RegisterSummary("Quantity", SummaryOperation.Sum, "ShipName");
helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipName");
helper.SetInvisibleColumnsWithoutGroupSummary();
helper.ApplyGroupSort();
}
我知道,这是一个很大的名字!生成的网格如下所示:
摘要操作
GridViewHelper有三个内置的摘要操作:总和,平均值和行数。一个非常有用的功能是定义自定义摘要操作的可能性。为此,我们需要为GridViewHelper提供两种方法。将为网格(或组)中找到的每一行调用一个方法,另一个方法将被调用以检索摘要操作的结果。下面我们有一个自定义摘要操作的示例。半虚拟操作将返回找到的最小值:
private List<int> mQuantities = new List<int>();
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterSummary("Quantity", SaveQuantity, GetMinQuantity);
}
private void SaveQuantity(string column, string group, object value)
{
mQuantities.Add(Convert.ToInt32(value));
}
private object GetMinQuantity(string column, string group)
{
int[] qArray = new int[mQuantities.Count];
mQuantities.CopyTo(qArray);
Array.Sort(qArray);
return qArray[0];
}
在上面的代码中,我们可以看到所需的方法签名。两者都接收汇总的组和列名称。如果摘要不是与组相关,则group参数将为null。为网格中找到的每一行调用的方法也接收当前行中列的值。
结果网格如下所示:
<强>限制强>
在一个示例中,我们说我们可以模拟分层分组。尽管网格似乎呈现分层分组,但实际实现不是分层的。没有组或子组。只有顺序注册的组。如果我们需要为内部组创建摘要,这将成为一个问题。下面我们可以看到在这种情况下会发生什么:
protected void Page_Load(object sender, EventArgs e)
{
GridViewHelper helper = new GridViewHelper(this.GridView1);
helper.RegisterGroup("ShipRegion", true, true);
helper.RegisterGroup("ShipName", true, true);
helper.RegisterSummary("ItemTotal", SummaryOperation.Sum, "ShipName");
helper.RegisterSummary("ItemTotal", SummaryOperation.Sum);
helper.GroupSummary += new GroupEvent(helper_Bug);
helper.ApplyGroupSort();
}
private void helper_Bug(string groupName, object[] values, GridViewRow row)
{
if (groupName == null) return;
row.BackColor = Color.Bisque;
row.Cells[0].HorizontalAlign = HorizontalAlign.Center;
row.Cells[0].Text = "[ Summary for " + groupName + " " + values[0] + " ]";
}
正如我们所看到的,摘要是在外部组的标题之后创建的。这是因为事件序列是:
Group1_Start
Group1_End
Group2_Start
Group2_End
对于分层分组,事件序列应为:
Group1_Start
Group2_Start
Group2_End
Group1_End
<强>实施强>
GridViewHelper是作为独立类而不是继承类实现的。这使得GridViewHelper可以与任何GridView一起使用,并且不会强制开发人员继承特定的GridView,这可能会影响类设计。解决方案中还有另外四个类:GridViewSummary,GridViewGroup,GridViewSummaryList和GridViewGroupList。 &#34;列表&#34;创建类以允许字符串索引器访问:helper.GeneralSummaries [&#34; ItemTotal&#34;]。值。
创建GridViewHelper时,会保存对目标GridView的引用,并且RowDataBound事件绑定到执行艰苦工作的方法:
public GridViewHelper(GridView grd, bool useFooterForGeneralSummaries, SortDirection groupSortDirection)
{
this.mGrid = grd;
this.useFooter = useFooterForGeneralSummaries;
this.groupSortDir = groupSortDirection;
this.mGeneralSummaries = new GridViewSummaryList();
this.mGroups = new GridViewGroupList();
this.mGrid.RowDataBound += new GridViewRowEventHandler(RowDataBoundHandler);
}
GridViewHelper内部使用的一些方法是公开定义的,因为它们提供了一些自定义可能需要的有用功能。还有一些其他选项未在示例中显示,但可以使用Visual Studio intellisense轻松验证。
已知问题
对于值类型的过度装箱和拆箱,性能可能会受到影响。为了解决这个问题,我们可以使用泛型实现内置的汇总操作,但这并不像我们希望的那样容易,正如在使用泛型计算中可以看到的那样。另一种可能性:运算符使用泛型进行重载。在现实生活中,这不会影响应用程序,除非有一百万行,或者有数千名用户同时分组和汇总数据。
联机示例使GridView EnableViewState保持为false。这是必需的,因为当EnableViewState为true时,如果页面在PostBack中,GridView将从ViewState重建,并且不会触发RowDataBound事件。我们可以在ASP.Net 2.0中安全地禁用ViewState,因为ControlState仍然会被保存。
答案 1 :(得分:4)
这并不完全回答这个问题,我在这里使用非规范化结果集而不是问题中的结果集,但是在这里调整数据不应该是主要问题。
以下是我如何进行我的分组,首先我附加到绑定查找组的行数据,并且我还劫持了渲染委托:
readonly Dictionary<Control, string> _groupNames = new Dictionary<Control, string>();
private Group _lastGroup;
protected void gv_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
var obj = (Obj)e.Row.DataItem;
if (obj.Group != _lastGroup)
{
e.Row.SetRenderMethodDelegate(RenderGridViewRowWithHeader);
_lastGroup = obj.Group;
// Cache group description for this row, note that you might
// like to implement this differently if you have your data normalized.
_groupNames[e.Row] = obj.Group.Description; }
}
}
}
然后在渲染方法中:
private void RenderGridViewRowWithHeader(HtmlTextWriter output, Control container)
{
// Render group header
var row = new TableRow { CssClass = "groupingCssClass" };
row.Cells.Add(new TableCell());
row.Cells.Add(new TableCell
{
ColumnSpan = ((GridViewRow)container).Cells.Count - 1,
Text = _groupNames[container]
});
row.RenderControl(output);
// Render row
container.SetRenderMethodDelegate(null); // avoid recursive call
container.RenderControl(output);
}
我知道这是一个黑客,但是当你深入细节时,ASP.NET就是这个。 此解决方案对控件状态非常友好,在运行时向网格添加行可能会导致异常行为。