[ASP.net] 巢狀GridView (塞資料行、塞資料列兩種寫法)

[ASP.net] 巢狀GridView (塞資料行、塞資料列兩種寫法)

GridView包GridView、GridView塞GridView,在網路上找到有兩種

1. 子GridView塞主GridView目前的資料行

2. 子GridView塞主GridView的下一資料列

 

 

第一種寫法:

這邊我就偷懶一下,拿MSDN論壇裡,TerryChuang大大的寫法來改:GridView裡的每一列,再塞入另一個GridView

原本按Close時,子GridView所在的資料行會一律隱藏,稍做改寫 判斷當都沒有子GridView顯示時,才把子GridView所在的資料行隱藏

.aspx


<%@ Page Language="C#" Debug="true" AutoEventWireup="true" CodeFile="subGridViewAddInColumn.aspx.cs" Inherits="subGridViewAddInColumn" %>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
 <title></title>
</head>
<body>
 <form id="form1" runat="server">
 
  <asp:GridView ID="GridView1" AutoGenerateColumns="false" runat="server" DataKeyNames="OrderID" 
   onrowcommand="GridView1_RowCommand" onrowcreated="GridView1_RowCreated">
   <Columns>
    <asp:BoundField HeaderText="OrderID" DataField="OrderID" />
    <asp:BoundField HeaderText="CustomerID" DataField="CustomerID" />
    <asp:TemplateField Visible="false">
     <ItemTemplate>
      <asp:GridView ID="GridView2" runat="server" Visible="false"  >
      </asp:GridView>
      <!--預設值Visible="false"是為了收合時判斷要不要把此資料行隱藏-->
     </ItemTemplate>
    </asp:TemplateField>
    <asp:ButtonField CommandName="Expand" HeaderText="異動" Text="Expand" ButtonType="Button" />
   </Columns>
  </asp:GridView>
 
 </form>
</body>
</html>

.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;

public partial class subGridViewAddInColumn : System.Web.UI.Page
{
    string Conn_E = WebConfigurationManager.ConnectionStrings["NorthwindChineseConnectionString"].ConnectionString;
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            GV1Bind();
        }

    }

    private void GV1Bind()
    {
       
            GridView1.DataSource = this.queryDataTable("Select * from Orders  Order by OrderID ASC");
            GridView1.DataBind();
        
    }


    protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
    {

        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            //Command按鈕的Argument給RowIndex,至於資料表的PK值,可以再利用GridView1.DataKeys[rowIndex].Value來取得
            (e.Row.Cells[3].Controls[0] as Button).CommandArgument = e.Row.RowIndex.ToString();

        }
    }


    protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        int rowIndex = int.Parse(e.CommandArgument.ToString());//RowIndex
        GridView gv2 = GridView1.Rows[rowIndex].FindControl("GridView2") as GridView;
        int orderID = int.Parse(GridView1.DataKeys[rowIndex].Value.ToString());

        Button btn = GridView1.Rows[rowIndex].Cells[3].Controls[0] as Button;

        
        //展開
        if (e.CommandName == "Expand")
        {
            gv2.DataSource = this.queryDataTable("Select * from OrderDetails Where OrderID = '"+orderID+"'");
            gv2.DataBind();
            gv2.Visible = true;
            
            GridView1.Columns[2].Visible = true;

            btn.Text = "Close";
            btn.CommandName = "Colse";
        }
        //收合
        else
        {

            int count = 0;
            foreach (GridViewRow gvr in GridView1.Rows)//走訪目前GridView1呈現的DataRow集合
            {
               GridView gv_2 = (GridView)gvr.FindControl("GridView2");
               if (gv_2.Visible==true)//至少有一個gv2有顯示,不知道為啥用DataSource當旗標來判斷會不準
               {
                   count++;
               }
             
            }

            //Debug用
            //Response.Write(count+"<br/>");
            if (count >= 2)
            {
                GridView1.Columns[2].Visible = true;
            }
            else
            {
                GridView1.Columns[2].Visible = false;
            }
             

            gv2.DataSource = null;
            gv2.DataBind();
            gv2.Visible = false;

            btn.Text = "Expand";
            btn.CommandName = "Expand";
        }
    }

    //撈出資料集
    public DataTable queryDataTable(string sql)
    {
        using (SqlConnection conn = new SqlConnection(this.Conn_E))
        {
            SqlDataAdapter da = new SqlDataAdapter(sql, conn);
            DataSet ds = new DataSet();
            da.Fill(ds);
            return (ds.Tables.Count > 0) ? ds.Tables[0] : new DataTable();

        }


    }
    
}

執行效果:

展開前兩列

image

收合第二列

image

再收合第一列(第3個資料行才隱藏)

image

 

第二種寫法(把子GridView塞在每一筆資料列的下一個資料列):

這邊本來使用[ASP.net] GridView新增、移除列來改寫,但發現實現結果,從上往下點展開要點兩次才展得開,由下往上點只要點一次就行(?)的奇怪現象

所以想來想去,還是在GridView1_RowDataBound事件,先把子GridView塞在每筆資料列的下一列,然後該列隱藏

讓按鈕只是個開關,用jQuery控制顯示和隱藏,這樣做每次展開、收合時不用跟Server端溝通,速度還比較快

.aspx


<%@ Page Language="C#" Debug="true" AutoEventWireup="true" CodeFile="GridViewRowAddByRow.aspx.cs" Inherits="GridViewRowAddByRow" %>



<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
 <title></title>
 <script type="text/javascript" src="js/jquery-1.4.1.min.js"></script>
 <script type="text/javascript">
     function showSubTable(index) {
         var targetTrClass = "master" + index + "_" + index;
         $("tr." + targetTrClass).toggle();


         /*改變input的文字*/
         var inputValue = $("input." + "master" + index).val();
         if (inputValue=="Open") {
             $("input." + "master" + index).val("Close");
         } else {
             $("input." + "master" + index).val("Open");
         }
     }
 </script>
</head>
<body>
 <form id="form1" name="form1" runat="server">


  <asp:GridView ID="GridView1" AutoGenerateColumns="False" runat="server"  onrowdatabound="GridView1_RowDataBound" >
   <Columns>
    <asp:BoundField HeaderText="OrderID" DataField="OrderID" />
    <asp:BoundField HeaderText="CustomerID" DataField="CustomerID" />
       <asp:TemplateField HeaderText="異動" ShowHeader="False">
           <ItemTemplate>
               <input type="button" onclick='showSubTable(<%# Container.DataItemIndex+1%>);' class='<%# "master" + (Container.DataItemIndex+1) %>'  value="Open" />
               <!--Label控制項給Server端讀取資訊用-->
               <asp:Label ID="Label1" runat="server" Text='<%# Container.DataItemIndex+1 %>'   ToolTip='<%# Eval("OrderID") %>' Visible="false" />
           </ItemTemplate>
       </asp:TemplateField>
   </Columns>
  </asp:GridView>

 </form>
</body>
</html>



.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Drawing;
using System.Threading;
using System.Data;
using System.Data.SqlClient;
using System.Web.Configuration;

public partial class GridViewRowAddByRow : System.Web.UI.Page
{
    string Conn_E = WebConfigurationManager.ConnectionStrings["NorthwindChineseConnectionString"].ConnectionString;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            GV1Bind();
        }

    }

    private void GV1Bind()
    {

        GridView1.DataSource = this.queryDataTable("Select * from Orders  Order by OrderID ASC");
        GridView1.DataBind();

    }

    int subRowCount = 0;
    protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        if (e.Row.RowType==DataControlRowType.DataRow)
        {
            TableCell cell = new TableCell();
            cell.ColumnSpan = 3;
            string index = (e.Row.FindControl("Label1") as Label).Text;
            GridView gv = new GridView(); 
            string OrderID = ((Label)e.Row.FindControl("Label1")).ToolTip;
            gv.DataSource = this.queryDataTable("Select * from OrderDetails Where OrderID = '" + OrderID + "'");
            gv.DataBind();
            cell.Controls.Add(gv);

            GridViewRow gvr = new GridViewRow(-1, 0, DataControlRowType.DataRow, DataControlRowState.Normal);
            gvr.Cells.Add(cell);
            gvr.CssClass = "master" + index + "_" + index;
            gvr.Attributes["style"] = "display:none;";

            subRowCount += 2;
            GridView1.Controls[0].Controls.AddAt(subRowCount, gvr);
            //GridView1.Controls[0]就是Table
            //e.Row.Parent.Controls.AddAt(subRowCount,gvr);//這樣寫也可以
            //e.Row.Parent就是Table
        }
    }


    //撈出資料集
    public DataTable queryDataTable(string sql)
    {
        using (SqlConnection conn = new SqlConnection(this.Conn_E))
        {
            SqlDataAdapter da = new SqlDataAdapter(sql, conn);
            DataSet ds = new DataSet();
            da.Fill(ds);
            return (ds.Tables.Count > 0) ? ds.Tables[0] : new DataTable();

        }


    }
}

執行效果:

展開前兩列

image

收合第二列

image

再收合第一列

image

 

程式碼懶人包下載


分享個人在實務上處理Master-Detail功能

其實都是準備兩支程式(.Net Team和Java Team都是這樣做)

Step 1. A程式列出主表資料

Step 2. 在A程式的每筆資料都會有個超連結以類似master.aspx?PK=value型式傳PK值,到B程式

Step 3. B程式利用Request.QueryString["PK"]讀取PK值(Java則是request.getParameter(String name)),來呈現Detail明細資料

這樣不管程式維護或使用者操作都比較簡單單純

不然頂多弄成這篇文章一樣[ASP.net] 使用ListView自己寫Master-Detail功能 - 不用jQuery Ajax的手風摺琴版(用到UpdatePanel,客戶還會以為畫面當掉)

Master-Detail功能,個人都是力求畫面&程式碼儘量簡潔好維護,也方便使用者

但也曾經碰過這樣的主管,很要求重視Table表格要有展開功能說這樣才能一覽該主表資料所對應的明細資料…

後來我實現該功能了(加上排序、分頁再加上Ajax不刷新),系統到使用者測試階段,一些User向我反應(包含那位天才主管= =”),怎麼系統畫面凌亂,好難操作

呃,所以回過頭來看以上的程式碼,有沒有發現Code-Behind都在處理一堆UI邏輯?

Code-Behind會處理UI邏輯通常是手寫美工人員給的HTML 字串(跑for迴圈產生HTML Table或動態更換圖片等等)

至少程式碼也較好懂

個人是不喜歡在Code-Behind處理UI事情,Code-Behind光是客戶要求的商業邏輯就夠複雜了

若再加上以上那些程式碼,糾結混合的結果,只會自己讓自己痛苦

引用某網友所講「是你自己束縛自己,還是主管客戶限制了你」

結論:這種GridView包GridView功能還是看看就好,實務上使用還是敬而遠之吧。

 

下麵這篇文章我看不明白,您能我寫個教程嗎?GridView分層嵌套