2017年7月14日 星期五

ASP.NET UpdatePanel + GridView 插入資料列、刪除資料列

參考資料1:Adding Dynamic Rows in ASP.Net GridView Control with TextBoxes (這篇是重點)
參考資料2:Sys.WebForms.PageRequestManager endRequest 事件

繼上一篇「ASP.NET GridView 按下按鈕,加入新的資料列」,在GridView下方有一個加入按鈕,按下「加入」按鈕後插入新的資料列,這次我想再加上一些功能,在每一列有刪除按鈕,按下「刪除」按鈕便能刪除GridView的資料列,為了不讓PostBack,刷新頁面,我打算加上UpdatePanel ;而在這個功能中,我有一個欄位是期間欄位,可以讓使用者去設定一段區間日期,為了作到這個效果我使用了bootstrap-datetimepicker套件,但是在測試的過程中,因為會插入新的資料列,而造成JQuery失效。

為什麼想製作這個功能,原因是有時候使用者可能只是先將資料key在一個資料表中,但是還沒真正的寫到資料庫裡,等於是暫時儲存起來,最後確認時,再送出所有的資料。

執行結果:

步驟1.先製作一個MasterPage1.master
<%@ Master Language="VB" CodeFile="MasterPage1.master.vb" Inherits="MasterPage1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head runat="server">
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>test_gv_addrow_delectrow</title>
    <link rel="stylesheet" href="css/bootstrap.css" />
    <link rel="stylesheet" href="css/CustStyle1.css"/>
    <script src="js/jquery-1.11.3.js"></script>
    <script src="js/bootstrap.js"></script>
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->
    <asp:ContentPlaceHolder id="head" runat="server">
    </asp:ContentPlaceHolder>
    <style type="text/css">
        body {
        background-color:#ffffff; /*背景顏色*/
        }
        .navbar {
        margin-bottom:0px;
        }
        .rowcol2 {
            padding-right:0px;
            padding-left:0px;
        }
        .custfooter {
            margin-top: 30px;
        }
    </style>
  </head>
  <body>
    <form id="form1" runat="server">
        <!--第一層-開始-->
        <nav class="navbar navbar-default">
            <div class="container-fluid">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    </button>
                    <a class="navbar-brand"><strong>test_gv_addrow_delectrow</strong></a>
                </div>
            </div>
        </nav>
        <!--第一層-結束-->
        <asp:ContentPlaceHolder id="CPH1" runat="server">
     
        </asp:ContentPlaceHolder>
        <div class="container-fluid">
        </div>
    </form>
  </body>
</html>

步驟2.建立test_gv_addrow_delectrow2.aspx
<%@ Page Title="" Language="VB" MasterPageFile="~/MasterPage1.master" AutoEventWireup="false" CodeFile="test_gv_addrow_delectrow2.aspx.vb" Inherits="test_gv_addrow_delectrow2" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
    <link rel="stylesheet" href="css/bootstrap-datetimepicker.min.css" />
    <script src="js/moment.min.js"></script>
    <script src="js/moment.zh-tw.js"></script>
    <script src="js/bootstrap-datetimepicker.min.js"></script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="CPH1" Runat="Server">
    <div class="container">
        <div class="row">
            <div class="col-xs-12">
                <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
                測試加入UpdatePanel
                <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
                    <ContentTemplate>
                        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="false" ShowFooter="true">
                            <Columns>
                                <asp:TemplateField HeaderText="">
                                    <ItemTemplate>
                                        <asp:Button ID="BDelete" runat="server" CommandName="dr" CssClass="btn btn-danger" Text="刪除" />
                                    </ItemTemplate>
                                </asp:TemplateField>
                                <asp:TemplateField HeaderText="順序號">
                                    <ItemTemplate>
                                        <asp:Label ID="L_order" runat="server"></asp:Label>
                                    </ItemTemplate>
                                </asp:TemplateField>
                                <asp:TemplateField HeaderText="期間">
                                    <ItemTemplate>
                                        <div class="form-group row has-success">
                                            <div class="col-xs-6">
                                                <asp:TextBox ID="TextBox1" runat="server" CssClass="form-control cust-date"></asp:TextBox>
                                            </div>
                                            <div class="col-xs-6">
                                                <asp:TextBox ID="TextBox2" runat="server" CssClass="form-control cust-date"></asp:TextBox>
                                            </div>
                                        </div>
                                    </ItemTemplate>
                                    <FooterStyle HorizontalAlign="left" />
                                    <FooterTemplate>
                                        <asp:Button ID="ButtonAdd" runat="server" CommandName="addrow" CssClass="btn btn-primary" Text="加入" />
                                    </FooterTemplate>
                                </asp:TemplateField>
                                <asp:TemplateField HeaderText="欄位2">
                                    <ItemTemplate>
                                        <div class="form-group row has-success">
                                            <div class="col-xs-12">
                                                <asp:TextBox ID="TextBox3" runat="server" CssClass="form-control"></asp:TextBox>
                                            </div>
                                        </div>
                                    </ItemTemplate>
                                </asp:TemplateField>
                                <asp:TemplateField HeaderText="欄位3">
                                    <ItemTemplate>
                                        <div class="form-group row has-success">
                                            <div class="col-xs-12">
                                                <asp:TextBox ID="TextBox4" runat="server" CssClass="form-control"></asp:TextBox>
                                            </div>
                                        </div>
                                    </ItemTemplate>
                                </asp:TemplateField>
                            </Columns>
                            <EmptyDataTemplate>
                                <asp:Label ID="GV_Empty" runat="server" Text="目前無資料"></asp:Label>
                            </EmptyDataTemplate>
                        </asp:GridView>
                        <script type="text/javascript">
                            $(document).ready(function () {
                                bind_TextBox();
                            });

                            //Sys.WebForms.PageRequestManager.getInstance是asp.net提供的
                            var prm = Sys.WebForms.PageRequestManager.getInstance();
                            if (prm != null) {
                                //add_endRequest 在完成非同步回傳之後,且已將控制項傳回瀏覽器時引發。
                                prm.add_endRequest(EndRequestHandler);
                            };
                            function EndRequestHandler(sender, args) {
                                if (typeof (args.get_error()) != 'undefined') {
                                    bind_TextBox(); //重新套用datetimepicker,如果不這麼作,所有的GridView中的TextBox將會失效
                                };
                            };
                         
                            //=========================================
                            //bind_TextBox2方法是最初的寫法,但是會有問題,按下加入按鈕後,JQuery就失效了,除了第一列的TextBox外,
                            //所以我改了另外一種寫法,就是在TextBox的CssClass屬性加入css的類別,再使用選擇器$('.cust-date')去抓取所有的TextBox並套用datetimepicker,可以看bind_TextBox方法
                            var str_arrid = [<%=str_js_arrid.ToString%>];
                            function bind_TextBox2(str_call) {
                                for (var x = 0; x <= str_arrid.length - 1; x++) {
                                    $('#' + str_arrid[x]).prop('readonly', true);
                                    $('#' + str_arrid[x]).datetimepicker({ locale: 'zh-tw', format: 'YYYY/MM/DD', ignoreReadonly: true });
                                };
                            };
                            <% str_js_arrid.Clear() : str_js_arrid = Nothing%>
                            //=========================================
                            function bind_TextBox() {
                                $('.cust-date').prop('readonly', true);
                                $('.cust-date').datetimepicker({ locale: 'zh-tw', format: 'YYYY/MM/DD', ignoreReadonly: true });
                            };
                        </script>
                    </ContentTemplate>
                    <Triggers>
                        <asp:AsyncPostBackTrigger ControlID="GridView1" EventName="RowCommand" />
                    </Triggers>
                </asp:UpdatePanel>
            </div>
        </div>
    </div>
</asp:Content>

說明:
1.黃色標起來的部分,是參考「參考資料2」,作用是為了讓datetimepicker重新被套用。
2.綠色標起來的部分,是我修改過後的寫法,可以正常運作。
3.紅色標起來的部分,是有問題的,當我按下加入按鈕後,只有第一列的日期還有作用,第一列之後加入的,就沒有任何作用,請注意看紅色註解。


步驟3.test_gv_addrow_delectrow2.aspx.vb
Imports System.Data
Partial Class test_gv_addrow_delectrow2
    Inherits System.Web.UI.Page
    Public str_js_arrid As StringBuilder = Nothing

    Private Sub test_gv_addrow_delectrow2_Load(sender As Object, e As EventArgs) Handles Me.Load
        If Not Page.IsPostBack Then
            '自行建立datatabel給GridView
            Dim cust_dt As DataTable = SetInitialRow()
            GridView1.DataSource = cust_dt
            GridView1.DataBind()
            GridView1.CssClass = "table table-bordered"
        End If
    End Sub

    '建立DataTable
    Function SetInitialRow() As DataTable
        Dim dt As DataTable = Nothing
        dt = New DataTable()
        Dim dr As DataRow = Nothing
        dt.Columns.Add(New DataColumn("Columns1", System.Type.GetType("System.Int32")))
        dt.Columns.Add(New DataColumn("date_start", System.Type.GetType("System.String")))
        dt.Columns.Add(New DataColumn("date_end", System.Type.GetType("System.String")))
        dt.Columns.Add(New DataColumn("Columns2", System.Type.GetType("System.String")))
        dt.Columns.Add(New DataColumn("Columns3", System.Type.GetType("System.String")))

        dr = dt.NewRow()
        dr("Columns1") = 0
        dr("date_start") = String.Empty
        dr("date_end") = String.Empty
        dr("Columns2") = String.Empty
        dr("Columns3") = String.Empty
        dt.Rows.Add(dr)
        'Store the DataTable in ViewState
        ViewState("CurrentTable") = dt

        Return dt

    End Function

    Sub AddNewRowToGrid()
        Dim rowIndex As Integer = 0
        If Not ViewState("CurrentTable") Is Nothing Then
            Dim dtCurrentTable As DataTable = Nothing
            dtCurrentTable = CType(ViewState("CurrentTable"), DataTable)
            Dim drCurrentRow As DataRow = Nothing

            If dtCurrentTable.Rows.Count > 0 Then
                '將目前的資料寫回DataTable
                For i As Integer = 1 To dtCurrentTable.Rows.Count
                    Dim TextBox1 As TextBox = CType(GridView1.Rows(rowIndex).Cells(1).FindControl("TextBox1"), TextBox)
                    Dim TextBox2 As TextBox = CType(GridView1.Rows(rowIndex).Cells(1).FindControl("TextBox2"), TextBox)
                    Dim TextBox3 As TextBox = CType(GridView1.Rows(rowIndex).Cells(2).FindControl("TextBox3"), TextBox)
                    Dim TextBox4 As TextBox = CType(GridView1.Rows(rowIndex).Cells(3).FindControl("TextBox4"), TextBox)

                    drCurrentRow = dtCurrentTable.NewRow()
                    dtCurrentTable.Rows(i - 1)("Columns1") = 0
                    dtCurrentTable.Rows(i - 1)("date_start") = TextBox1.Text
                    dtCurrentTable.Rows(i - 1)("date_end") = TextBox2.Text
                    dtCurrentTable.Rows(i - 1)("Columns2") = TextBox3.Text
                    dtCurrentTable.Rows(i - 1)("Columns3") = TextBox4.Text
                    rowIndex = rowIndex + 1
                Next
                dtCurrentTable.Rows.Add(drCurrentRow)
                ViewState("CurrentTable") = dtCurrentTable

                GridView1.DataSource = dtCurrentTable
                GridView1.DataBind()
                GridView1.CssClass = "table table-bordered"


            End If
        Else
            Response.Write("ViewState is null")
        End If

        'Set Previous Data on Postbacks
        SetPreviousData()
    End Sub

    Sub SetPreviousData()
        Dim rowIndex As Integer = 0
        If Not ViewState("CurrentTable") Is Nothing Then
            Dim dt As DataTable = Nothing
            dt = CType(ViewState("CurrentTable"), DataTable)
            If dt.Rows.Count > 0 Then
                For i As Integer = 0 To dt.Rows.Count - 1

                    Dim TextBox1 As TextBox = CType(GridView1.Rows(rowIndex).Cells(1).FindControl("TextBox1"), TextBox)
                    Dim TextBox2 As TextBox = CType(GridView1.Rows(rowIndex).Cells(1).FindControl("TextBox2"), TextBox)
                    Dim TextBox3 As TextBox = CType(GridView1.Rows(rowIndex).Cells(2).FindControl("TextBox3"), TextBox)
                    Dim TextBox4 As TextBox = CType(GridView1.Rows(rowIndex).Cells(3).FindControl("TextBox4"), TextBox)

                    TextBox1.Text = dt.Rows(i)("date_start").ToString()
                    TextBox2.Text = dt.Rows(i)("date_end").ToString()
                    TextBox3.Text = dt.Rows(i)("Columns2").ToString()
                    TextBox4.Text = dt.Rows(i)("Columns3").ToString()

                    rowIndex = rowIndex + 1
                Next
            End If
        End If
    End Sub

    Private Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
        If e.Row.RowType = DataControlRowType.Header Then
            e.Row.CssClass = "bg-primary"
            e.Row.Cells(0).CssClass = "col-xs-1 col-md-1"
            e.Row.Cells(1).CssClass = "col-xs-1 col-md-1"
            e.Row.Cells(2).CssClass = "col-xs-2 col-md-2"
            e.Row.Cells(3).CssClass = "col-xs-1 col-md-1"
            e.Row.Cells(4).CssClass = "col-xs-1 col-md-1"
            str_js_arrid = New StringBuilder()
            str_js_arrid.Append("")
        End If
        If e.Row.RowType = DataControlRowType.DataRow Then
            Dim L_order As Label = CType(e.Row.FindControl("L_order"), Label)
            Dim TextBox1 As TextBox = CType(e.Row.FindControl("TextBox1"), TextBox)
            Dim TextBox2 As TextBox = CType(e.Row.FindControl("TextBox2"), TextBox)
            If str_js_arrid.ToString = "" Then
                str_js_arrid.Append("'" & TextBox1.ClientID & "','" & TextBox2.ClientID & "'")
            Else
                str_js_arrid.Append(",'" & TextBox1.ClientID & "','" & TextBox2.ClientID & "'")
            End If
            TextBox1.Attributes.Add("placeholder", "請選擇開始期間日期")
            TextBox2.Attributes.Add("placeholder", "請選擇結束期間日期")
            L_order.Text = e.Row.RowIndex + 1
        End If
    End Sub

    Private Sub GridView1_RowCommand(sender As Object, e As GridViewCommandEventArgs) Handles GridView1.RowCommand
        If e.CommandName = "dr" Or e.CommandName = "addrow" Then
            Dim str_commandName As String = e.CommandName
            Dim BS As Button = CType(e.CommandSource, Button) '先取得命令的來源並轉換成按鈕
            Dim GV_Row As GridViewRow = CType(BS.NamingContainer, GridViewRow) '將Button轉換成GridViewRow就是您所點選的某一列

            If e.CommandName = "dr" Then
                Delete_RowToGrid(GV_Row.RowIndex)
            ElseIf e.CommandName = "addrow" Then
                AddNewRowToGrid()
            End If
        End If
    End Sub

    Sub Delete_RowToGrid(ByVal int_delet_row As Integer)

        If Not ViewState("CurrentTable") Is Nothing Then
            Dim dtCurrentTable As DataTable = Nothing
            dtCurrentTable = CType(ViewState("CurrentTable"), DataTable)

            If dtCurrentTable.Rows.Count > 0 Then
                '將目前的資料寫回DataTable
                For i As Integer = 0 To dtCurrentTable.Rows.Count - 1
                    If i <> int_delet_row Then
                        Dim TextBox1 As TextBox = CType(GridView1.Rows(i).Cells(1).FindControl("TextBox1"), TextBox)
                        Dim TextBox2 As TextBox = CType(GridView1.Rows(i).Cells(1).FindControl("TextBox2"), TextBox)
                        Dim TextBox3 As TextBox = CType(GridView1.Rows(i).Cells(2).FindControl("TextBox3"), TextBox)
                        Dim TextBox4 As TextBox = CType(GridView1.Rows(i).Cells(3).FindControl("TextBox4"), TextBox)

                        dtCurrentTable.Rows(i)("date_start") = TextBox1.Text
                        dtCurrentTable.Rows(i)("date_end") = TextBox2.Text
                        dtCurrentTable.Rows(i)("Columns2") = TextBox3.Text
                        dtCurrentTable.Rows(i)("Columns3") = TextBox4.Text

                    Else
                        '要刪除的資料不紀錄
                    End If

                Next

                dtCurrentTable.Rows(int_delet_row).Delete() '刪除資料列
                If dtCurrentTable.Rows.Count = 0 Then
                    dtCurrentTable.Clear()
                    dtCurrentTable = Nothing
                    dtCurrentTable = SetInitialRow()
                Else
                    ViewState("CurrentTable") = dtCurrentTable
                End If

                GridView1.DataSource = dtCurrentTable
                GridView1.DataBind()
                GridView1.CssClass = "table table-bordered"
            End If
        Else
            Response.Write("ViewState is null")
        End If

        'Set Previous Data on Postbacks
        SetPreviousData()
    End Sub
End Class

沒有留言:

張貼留言