11

我有两个存储过程,它们返回两组相关数据。数据是这样的。第一个过程返回这样的数据

ISSUE_ID          ISSUETYPE          
-------------------------------------
1            ISSUE 1 TYPE
2            ISSUE 2 TYPE
3            ISSUE 3 TYPE
4            ISSUE 4 TYPE

Second Procedure 根据 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中显示。

ISSUE 1 TYPE
-----------------------------
            HEADER 1 NAME                 
            HEADER 2 NAME 
ISSUE 2 TYPE
-----------------------------
            HEADER 3 NAME                 
            HEADER 4 NAME                  
ISSUE 3 TYPE
-----------------------------
            HEADER 5 NAME                 

提前感谢一百万..需要一些建议来实现这一目标。

4

2 回答 2

10

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>

ASP.Net GridView 中的分组

笔记:

  • 主要逻辑在GridView的RowCreated和RowDataBound事件中。

  • 在遍历我的所有行时

    • 查看 CustomerId(主索引)并检查其他行。
    • 跟踪正在运行的总计
    • 跟踪正在运行的小计
  • 在遍历结果集时,主索引的每一点都会发生变化:

    • 添加小计行
    • 为下一组重置小计
  • 标题在 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 方法采用了另一个参数。该参数指定必须创建摘要的组的名称。组名是根据组列名自动生成的。如果组只有一列,则组名将是该列的名称。如果组有多个列,组名称将是组成组的列的有序连接,并用加号 (“+”) 连接:“ShipRegion+ShipName”。

我们可以在网格下方看到分组和组摘要:

在此处输入图像描述

可以在网格中创建多个组,模拟分层分组,如下所示:

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 具有允许轻松实现视觉或功能调整的事件。事件列表如下:

  • GroupStart:在新组开始时发生,这意味着在组列中找到新值时。
  • GroupEnd:发生在组的最后一行
  • GroupHeader:为组添加自动标题行时发生。如果组不是自动的,则不会触发事件。
  • GroupSummary:为组生成摘要行时发生。如果该组不是自动的,则不会触发该事件,但如果该组是一个抑制组,则会触发该事件(稍后会看到)。
  • GeneralSummary:在计算一般摘要之后发生。如果摘要是自动的,则在添加摘要行和将摘要值放置在行中之后发生事件。
  • FooterDataBound:发生在页脚数据绑定中。

通过多几行代码,我们可以改善网格的视觉效果:

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 = "&nbsp;&nbsp;" + row.Cells[0].Text;
    }
    else if (groupName == "ShipName")
    {
        row.BackColor = Color.FromArgb(236, 236, 236);
        row.Cells[0].Text = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + 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 不知道如何继续将在组行中找到的值汇总为唯一值。这提醒了某些已知消息:

“列 'column_name' 在选择列表中无效,因为它不包含在聚合函数或 GROUP BY 子句中。”

显示没有汇总操作的列是没有意义的,要隐藏它们我们需要调用一个方法:

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 参数将为空。为网格中找到的每一行调用的方法还接收当前行中列的值。

生成的网格如下所示:

在此处输入图像描述

限制

在一个示例中,我们说我们可以模拟分层分组。尽管网格似乎呈现出分层分组,但实际实现并不是分层的。没有组或子组。只有顺序注册的组。如果我们需要为内部组创建摘要,这将成为一个问题。下面我们可以看到在这种情况下会发生什么:

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。创建“列表”类以允许字符串索引器访问:helper.GeneralSummaries["ItemTotal"].Value。

创建 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 智能感知轻松验证。

已知的问题

值类型的过度装箱和拆箱可能会影响性能。为了解决这个问题,我们可以使用泛型实现内置的汇总操作,但这并不像我们希望的那样容易,正如在 Using Generics for Calculations 中看到的那样。另一种可能性:使用泛型的运算符重载。在现实生活中,这不会影响应用程序,除非有一百万行,或者有数千个用户同时对数据进行分组和汇总。

在线示例将 GridView EnableViewState 保持为 false。这是必需的,因为当 EnableViewState 为 true 时,如果页面处于 PostBack 中,则 GridView 将从 ViewState 中重建,并且不会触发 RowDataBound 事件。我们可以在 ASP.Net 2.0 中安全地禁用 ViewState,因为 ControlState 仍将被保存。

于 2014-01-20T11:46:50.333 回答
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);
}

我知道这是一个 hack,但是当您深入了解 ASP.NET 时,这就是全部。此解决方案对控件状态非常友好,在运行时向网格添加行可能会导致异常行为。

于 2015-05-23T22:14:38.673 回答