[ASP.NET]Gridview+Formview實現無刷新更新

[ASP.NET]Gridview+Formview實現無刷新更新

前陣子在公司內處理一個案子,客戶希望在一個畫面中能同時有Grid跟Detail fields,當初第一個直覺想到的就是透過Gridview搭配Formview來實現,但客戶接著又提了一個需求,說希望達到畫面無刷新的編輯與更新,也因此我寫了下面這個sample給專案團隊,請他們在做些修飾,以下我描述一下我進行的動作。

使用元件:UpdataPanel、Gridview、Formview、SqlDataSource

使用資料:Northwind中的Products資料表(由於Products資料表本身有一些contraints,所以我把資料倒到自己建立的Product資料表中)

 

《aspx》

  • Gridview:
    • 設定DataSource為SqlDataSource1,PageSize=5
    • 多一個ItemTemplate,裡頭放兩個按鈕,一個是編輯,另一個是刪除,CommandName分別設定Edit跟Delete
            CellPadding="4" DataKeyNames="ProductID" DataSourceID="SqlDataSource1" ForeColor="#333333"
            GridLines="None" Height="253px" PageSize="5" Width="554px" OnRowCommand="GridView1_RowCommand" OnRowDataBound="GridView1_RowDataBound">
            <RowStyle BackColor="#EFF3FB" />
            <Columns>
            <asp:TemplateField ShowHeader="False">  
                   <ItemTemplate>  
                        <asp:Button ID="btnEdit" runat="server" CausesValidation="False" CommandName="Edit"  
                            Text="編輯"></asp:Button>  
                        <asp:Button ID="btnDelete" runat="server" CausesValidation="False" CommandName="Delete"  
                            Text="刪除"></asp:Button>  
                    </ItemTemplate>  
                </asp:TemplateField> 
                <asp:BoundField DataField="ProductID" HeaderText="ProductID" ReadOnly="True" SortExpression="ProductID" />
                <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
                <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" SortExpression="SupplierID" />
                <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" SortExpression="CategoryID" />
            </Columns>
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <EditRowStyle BackColor="#2461BF" />
            <AlternatingRowStyle BackColor="White" />
        </asp:GridView>
  • Formview
    • 設定DataSource為SqlDataSource1,DefaultMode=Edit,裡頭繫結四個欄位
    • 在下方放置兩個LinButton,用來做更新與取消,Command分別設為Update與Cancel
            DataSourceID="SqlDataSource1" DefaultMode="Edit" ForeColor="#333333" Visible="False">
            <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
            <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
            <EditItemTemplate>
                <table style="width: 400px">
                    <tr>
                        <td style="width: 100px; text-align: left">
                            ProductID</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtProductID" runat="server" Text='<%# Bind("ProductID") %>'></asp:TextBox></td>
                    </tr>
                    <tr>
                        <td style="width: 100px; text-align: left">
                            ProductName</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtProductName" runat="server" Text='<%# Bind("ProductName") %>'></asp:TextBox></td>
                    </tr>
                    <tr>
                        <td style="width: 100px; text-align: left">
                            SupplierID</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtSupplierID" runat="server" Text='<%# Bind("SupplierID") %>'></asp:TextBox></td>
                    </tr>
                    <tr>
                        <td style="width: 100px; text-align: left">
                            CategoryID</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtCategoryID" runat="server" Text='<%# Bind("CategoryID") %>'></asp:TextBox></td>
                    </tr>
                </table>
                <asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update"
                    Text="更新"></asp:LinkButton>
                <asp:LinkButton ID="CancelButton" runat="server" CausesValidation="False" CommandName="Cancel"
                    Text="取消"></asp:LinkButton>
            </EditItemTemplate>
            <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
            <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
        </asp:FormView>
  • SqlDataSource
    • 分別設定好SelectCommand、DeleteCommand、UpdateCommand
            SelectCommand="SELECT * FROM [product]"
                        DeleteCommand="DELETE FROM [product] WHERE [ProductID] = @ProductID" 
                        UpdateCommand="UPDATE [product] SET [ProductName] = @ProductName, [SupplierID] = @SupplierID, [CategoryID] = @CategoryID WHERE [ProductID] = @ProductID" 
                        OldValuesParameterFormatString="{0}">
            <DeleteParameters>
                <asp:Parameter Name="ProductID" Type="Int32" />
            </DeleteParameters>
            <UpdateParameters>
                <asp:Parameter Name="ProductName" Type="String" />
                <asp:Parameter Name="SupplierID" Type="String" />
                <asp:Parameter Name="CategoryID" Type="String" />
            </UpdateParameters>
            </asp:SqlDataSource>

 

《cs》

  • RowDataBound
    • 為了在按下「編輯」鈕時可以將筆資料的明細帶到Formview中,所以在 RowDataBound 事件中,去設定「編輯」鈕的 CommandArgument 屬性值為 RowIndex。
    {
        Button tButton = new Button();

        //如果是DataRow
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            //設定編輯鈕的 CommandArgument
            tButton = (Button)e.Row.Cells[0].FindControl("btnEdit");
            //按下Edit按鈕時,CommandArgument為該筆資料的index
            tButton.CommandArgument = e.Row.RowIndex.ToString();
        }
    }
  • RowCommand
    • 接著在RowCommand的事件中加入按下「編輯」鈕時,要將Gridview點選的資料帶入Formview中,並將Formview的模式切換為編輯模式
    {
        int tEditIndex=0;
        
        switch(e.CommandName)
        {
            case "Edit":
                //根據有沒有分頁來取得目前實際的index,避免換頁後取錯資料
                tEditIndex = getNowIndex(Convert.ToInt32(e.CommandArgument));
                FormView1.PageIndex = tEditIndex;
                FormView1.ChangeMode(FormViewMode.Edit); //將FormView切換到編輯模式
                FormView1.Visible = true;
                break;
            default:
                break;

        }
    }

    /// <summary>
    /// 傳入目前頁面上的index,回傳實際的index
    /// </summary>
    /// <param name="pRowIndex">目前頁面上的index</param>
    /// <returns>根據分頁來計算實際的index</returns>
    private int getNowIndex(int pRowIndex)
    {
        int tEditIndex=0;

        if (GridView1.AllowPaging)
        {
            //GridView有分頁時,index=頁次*分頁大小+頁面的index
            tEditIndex = (GridView1.PageIndex) * GridView1.PageSize + pRowIndex;
        }
        else
        {
            //GridView無分頁時,直接使用pRowIndex
            tEditIndex = pRowIndex;
        }

        return tEditIndex;
    }

 

進行到這邊,只寫幾行code,我們已經可以順利的編輯Product這個Table的資料了,如下圖:

image 刪除後也會自動帶下一筆資料:

image但做到這邊可以交差了嗎?還沒,因為客戶要求要無刷新的操作,期望的結果是:

  1. 在Gridview中按下編輯時,將該筆資料帶到Formview中,不能全頁刷新,只刷新Formview(條件一)
  2. 在Gridview中按下刪除時,將該筆資料從Gridview中刪除,不能全頁刷新,刷新Gridview與Formview(條件二)
  3. 在Formview中按下更新,該筆資料會被更新回Gridview中,不能全頁刷新,只刷新Gridview(條件三)

所以我又做了幾個小動作,UpdatePanel這東西這麼好用,怎麼可以不用呢? 我們依以下步驟進行設定:

  • 在畫面上拉一個ScriptManager
  • 在畫面上拉一個UpdatePanel,名稱為UpdatePanel2,將Gridview放到裡頭

image

    • 設定UpdatePanel的屬性:需特別留意
      • ChildAsTriggers要設定為False,否則當您按下編輯或者刪除鈕時,Gridview都會被更新
      • UpdateMode設定為Conditional,否則只要有trigger被觸發時,則這個UpdatePanel不管怎樣都會刷新

image

    • 設定UpdatePanel的Triggers,我總共加了兩個Trigger
      • 第一個是Gridview1的RowDeleted,為了滿足條件二

image

      • 第二個是FormView1的ItemUpdate,為了滿足條件三

image

  • 再拉一個UpdatePanel,名稱為UpdatePanel1,將Formview放在裡頭

image

    • 一般屬性設定上與UpdatePanel2相同

image

    • 而在Triggers的設定上略有不同,一樣有兩個Triggers:
      • 第一個是Gridview1的RowEditing,為了滿足條件一

image

      • 第二個是Gridview1的RowDeleted,為了滿足條件二

image

做完這些動作後,我們再操作一下,果然發現我們可以做到部分刷新而完成資料的更新,這也算是單單使用微軟提供的元件來達到客戶想要的效果了,不過這一段程式碼後來專案成員有再做一些操作性的改良,我也沒深究,這邊PO給大家參考,但如果要照抄的話,建議您再測試看看,以下為完整的程式碼:

《aspx》

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>未命名頁面</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <br />
        <asp:UpdatePanel ID="UpdatePanel2" runat="server" ChildrenAsTriggers="False" UpdateMode="Conditional">
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="GridView1" EventName="RowDeleted" />
                <asp:AsyncPostBackTrigger ControlID="FormView1" EventName="ItemUpdated" />
            </Triggers>
            <ContentTemplate>
        <asp:GridView ID="GridView1" runat="server" AllowPaging="True" AutoGenerateColumns="False"
            CellPadding="4" DataKeyNames="ProductID" DataSourceID="SqlDataSource1" ForeColor="#333333"
            GridLines="None" Height="253px" PageSize="5" Width="554px" OnRowCommand="GridView1_RowCommand" OnRowDataBound="GridView1_RowDataBound">
            <RowStyle BackColor="#EFF3FB" />
            <Columns>
            <asp:TemplateField ShowHeader="False">  
                   <ItemTemplate>  
                        <asp:Button ID="btnEdit" runat="server" CausesValidation="False" CommandName="Edit"  
                            Text="編輯"></asp:Button>  
                        <asp:Button ID="btnDelete" runat="server" CausesValidation="False" CommandName="Delete"  
                            Text="刪除"></asp:Button>  
                    </ItemTemplate>  
                </asp:TemplateField> 
                <asp:BoundField DataField="ProductID" HeaderText="ProductID" ReadOnly="True" SortExpression="ProductID" />
                <asp:BoundField DataField="ProductName" HeaderText="ProductName" SortExpression="ProductName" />
                <asp:BoundField DataField="SupplierID" HeaderText="SupplierID" SortExpression="SupplierID" />
                <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" SortExpression="CategoryID" />
            </Columns>
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <EditRowStyle BackColor="#2461BF" />
            <AlternatingRowStyle BackColor="White" />
        </asp:GridView>
         </ContentTemplate>
        </asp:UpdatePanel>
        <br />
    </div>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="False">
            <ContentTemplate>
        <asp:FormView ID="FormView1" runat="server" CellPadding="4" DataKeyNames="ProductID"
            DataSourceID="SqlDataSource1" DefaultMode="Edit" ForeColor="#333333" Visible="False">
            <FooterStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
            <RowStyle BackColor="#FFFBD6" ForeColor="#333333" />
            <EditItemTemplate>
                <table style="width: 400px">
                    <tr>
                        <td style="width: 100px; text-align: left">
                            ProductID</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtProductID" runat="server" Text='<%# Bind("ProductID") %>'></asp:TextBox></td>
                    </tr>
                    <tr>
                        <td style="width: 100px; text-align: left">
                            ProductName</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtProductName" runat="server" Text='<%# Bind("ProductName") %>'></asp:TextBox></td>
                    </tr>
                    <tr>
                        <td style="width: 100px; text-align: left">
                            SupplierID</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtSupplierID" runat="server" Text='<%# Bind("SupplierID") %>'></asp:TextBox></td>
                    </tr>
                    <tr>
                        <td style="width: 100px; text-align: left">
                            CategoryID</td>
                        <td style="width: 392px; text-align: left">
                            <asp:TextBox ID="txtCategoryID" runat="server" Text='<%# Bind("CategoryID") %>'></asp:TextBox></td>
                    </tr>
                </table>
                <asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update"
                    Text="更新"></asp:LinkButton>
                <asp:LinkButton ID="CancelButton" runat="server" CausesValidation="False" CommandName="Cancel"
                    Text="取消"></asp:LinkButton>
            </EditItemTemplate>
            <PagerStyle BackColor="#FFCC66" ForeColor="#333333" HorizontalAlign="Center" />
            <HeaderStyle BackColor="#990000" Font-Bold="True" ForeColor="White" />
        </asp:FormView>
            </ContentTemplate>
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="GridView1" EventName="RowEditing" />
                <asp:AsyncPostBackTrigger ControlID="GridView1" EventName="RowDeleted" />
            </Triggers>
        </asp:UpdatePanel>
        <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
            SelectCommand="SELECT * FROM [product]"
                        DeleteCommand="DELETE FROM [product] WHERE [ProductID] = @ProductID" 
                        UpdateCommand="UPDATE [product] SET [ProductName] = @ProductName, [SupplierID] = @SupplierID, [CategoryID] = @CategoryID WHERE [ProductID] = @ProductID" 
                        OldValuesParameterFormatString="{0}">
            <DeleteParameters>
                <asp:Parameter Name="ProductID" Type="Int32" />
            </DeleteParameters>
            <UpdateParameters>
                <asp:Parameter Name="ProductName" Type="String" />
                <asp:Parameter Name="SupplierID" Type="String" />
                <asp:Parameter Name="CategoryID" Type="String" />
            </UpdateParameters>
            </asp:SqlDataSource>
    </form>
</body>
</html>

《cs》

using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

public partial class PartialForm : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        Button tButton = new Button();

        //如果是DataRow
        if (e.Row.RowType == DataControlRowType.DataRow)
        {
            //設定編輯鈕的 CommandArgument
            tButton = (Button)e.Row.Cells[0].FindControl("btnEdit");
            //按下Edit按鈕時,CommandArgument為該筆資料的index
            tButton.CommandArgument = e.Row.RowIndex.ToString();
        }
    }

    protected void GridView1_RowCommand(object sender, GridViewCommandEventArgs e)
    {
        int tEditIndex=0;
        
        switch(e.CommandName)
        {
            case "Edit":
                //根據有沒有分頁來取得目前實際的index,避免換頁後取錯資料
                tEditIndex = getNowIndex(Convert.ToInt32(e.CommandArgument));
                FormView1.PageIndex = tEditIndex;
                FormView1.ChangeMode(FormViewMode.Edit); //將FormView切換到編輯模式
                FormView1.Visible = true;
                break;
            default:
                break;

        }
    }

    /// <summary>
    /// 傳入目前頁面上的index,回傳實際的index
    /// </summary>
    /// <param name="pRowIndex">目前頁面上的index</param>
    /// <returns>根據分頁來計算實際的index</returns>
    private int getNowIndex(int pRowIndex)
    {
        int tEditIndex=0;

        if (GridView1.AllowPaging)
        {
            //GridView有分頁時,index=頁次*分頁大小+頁面的index
            tEditIndex = (GridView1.PageIndex) * GridView1.PageSize + pRowIndex;
        }
        else
        {
            //GridView無分頁時,直接使用pRowIndex
            tEditIndex = pRowIndex;
        }

        return tEditIndex;
    }
}

游舒帆 (gipi)

探索原力Co-founder,曾任TutorABC協理與鼎新電腦總監,並曾獲選兩屆微軟最有價值專家 ( MVP ),離開職場後創辦探索原力,致力於協助青少年培養面對未來的能力。認為教育與組織育才其實息息相關,都是在為未來儲備能量,2018年起成立為期一年的專題課程《職涯躍升的關鍵24堂課》,為培養台灣未來的領袖而努力。