在Asp.Net Gridview中对数据进行分组

Nag*_*Nag 10 c# asp.net gridview

我有两个存储过程,它们返回两组相关数据.数据是这样的.First Procedure返回这样的数据

ISSUE_ID          ISSUETYPE          
-------------------------------------
1            ISSUE 1 TYPE
2            ISSUE 2 TYPE
3            ISSUE 3 TYPE
4            ISSUE 4 TYPE
Run Code Online (Sandbox Code Playgroud)

第二个过程基于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
Run Code Online (Sandbox Code Playgroud)

事情是我如何基于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                 
Run Code Online (Sandbox Code Playgroud)

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

mij*_*ved 8

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>
Run Code Online (Sandbox Code Playgroud)

在ASP.Net GridView中进行分组

笔记:

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

  • 迭代我所有的行

    • 偷看CustomerId(主索引)并检查其他行.
    • 跟踪正在运行的GrandTotal(s)
    • 跟踪正在运行的SubTotal(s)
  • 在迭代结果集时,主索引的每一点都会发生变化:

    • 添加SubTotal(s)行
    • 重置SubTotal准备好下一组
  • 标题显示为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);
}
Run Code Online (Sandbox Code Playgroud)

首先,我们创建GridViewHelper,设置它将在构造函数中运行的网格.然后我们注册指定要执行的列名和摘要操作的摘要.结果如下:

在此输入图像描述

在此示例中,添加了一个新行以显示摘要.另一个选项是使用页脚行显示摘要,而不是创建新摘要.将新行添加到网格时,仅创建显示汇总列的必需单元格.使用页脚,创建所有单元格.在组摘要的情况下,所有单元的生成或仅所需单元的生成是组属性.

现在我们将创建一个组.代码如下所示:

protected void Page_Load(object sender, EventArgs e)
{
    GridViewHelper helper = new GridViewHelper(this.GridView1);
    helper.RegisterGroup("ShipRegion", true, true);
    helper.ApplyGroupSort();
}
Run Code Online (Sandbox Code Playgroud)

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();
}
Run Code Online (Sandbox Code Playgroud)

这次,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();
}
Run Code Online (Sandbox Code Playgroud)

结果:

在此输入图像描述

当有多个组时,可视化会受到影响.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

化妆品后的网格:

在此输入图像描述

更多分组选项

还有两个有趣的样本.第一个是复合组.第二个定义了一个抑制组,它具有与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();
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我们可以向该组添加摘要.这次我们将定义一个平均操作并添加一个标签来指示操作:

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";
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

最后一个样本将创建一个抑制组.值得一提的是,如果定义了抑制组,则不会创建其他组.同样,如果已经定义了一个组,我们就无法创建一个抑制组,如果我们尝试它就会引发异常.

下面我们可以看到抑制组的代码和网格外观:

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();
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

没有定义汇总操作的列不显示任何值.这是有道理的,因为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();
}
Run Code Online (Sandbox Code Playgroud)

我知道,这是一个很大的名字!生成的网格如下所示:

在此输入图像描述

摘要操作

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];
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,我们可以看到所需的方法签名.两者都接收汇总的组和列名称.如果摘要不是与组相关,则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] + " ]";
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

我们可以看到,摘要是在外部组的标题之后创建的.发生这种情况是因为事件序列是:

Group1_Start
Group1_End
Group2_Start
Group2_End
Run Code Online (Sandbox Code Playgroud)

对于分层分组,事件序列应为:

Group1_Start
Group2_Start
Group2_End
Group1_End
Run Code Online (Sandbox Code Playgroud)

履行

GridViewHelper是作为独立类而不是继承类实现的.这使得GridViewHelper可以与任何GridView一起使用,并且不会强制开发人员继承特定的GridView,这可能会影响类设计.解决方案中还有另外四个类:GridViewSummary,GridViewGroup,GridViewSummaryList和GridViewGroupList.创建"list"类以允许字符串索引器访问: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);
}
Run Code Online (Sandbox Code Playgroud)

GridViewHelper内部使用的一些方法是公开定义的,因为它们提供了一些自定义可能需要的有用功能.还有一些其他选项未在示例中显示,但可以使用Visual Studio intellisense轻松验证.

已知的问题

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

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

  • 这个`GridViewHelper` 类是您创建的某种自定义工具吗?它似乎不是标准库的一部分,但我没有看到任何关于您从这里获取它的参考。 (2认同)