2017年6月25日 星期日

ASP.NET 使用JS達到Shift鍵加滑鼠左鍵讓Table中的CheckBox連續選取

這個需求是要模仿電子信箱中的CheckBox(我是參考GMail),勾選了第1筆之後,Shift鍵+滑鼠左鍵勾選第2筆,可以勾選我們要的多筆資料,稱這種功能為「連續選取」(我沒說錯吧)。
如果有想嘗試的人,可以先去我的前一篇(ASP.NET Table中的刪除按鈕,顯示2次提示訊息)複製SQL CREATE TABLE指令先建立資料表,所以後端程式碼我就不貼出來了,因為差不多一樣只是這次的重點在JavaScript怎麼達到連續選取的功能,說明都寫在註解中

test_gv_checkbox.aspx
前端
<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/MasterPage1.master" CodeBehind="test_gv_checkbox.aspx.vb" Inherits="test_gridview_dialog.test_gv_checkbox" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
        <style type="text/css">
        /*test_gv_dialog.aspx 只要是要使用在這個網頁的css都寫在此處*/
        /*設定CheckBox大小,套用的對象有2個
          1.只要是input的checkbox都套用。
          2.因為GridView的CheckBox CssClass屬性有設定CSS,經過轉譯後CheckBox會被span標籤包起來,
            所以只要是span標籤class是rowcb底下的CheckBox都套用。
        */
        input[type="checkbox"], span[class="rowcb"]>input[type="checkbox"]{
          width: 20px;
          height: 20px;
        }
    </style>
    <script type="text/javascript">
        //test_gv_dialog.aspx JavaScript只要是要使用在這個網頁的JavaScript都寫在此處
        var shiftkeydown = false; //紀錄使用者目前有沒有按下Shift鍵
        $(document).ready(function () {
            //只要是CSS類別名稱為GV1_headcb,底下的CheckBox就註冊change事件
            $('.GV1_headcb > input[type="checkbox"]').change(function () {
                var bool_change;
                bool_change = this.checked; //取得自己勾選狀態
                //table底下的tbody底下的tr CheckBox透過each(類似迴圈),將table中所有的CheckBox設定為勾選或取消勾選
                $('#<%=GridView1.ClientID %> > tbody > tr input[type="checkbox"]').each(function () {
                    $(this).prop('checked', bool_change); //設定CheckBox狀態
                });
            });
            //註冊keydown keyup事件,function名稱為listening_for_shift
            $(document).on('keydown keyup', listening_for_shift);
            //為GridView註冊change事件,對象是所有的CheckBox
            $('#<%=GridView1.ClientID %>').on('change', 'input[type="checkbox"]', function () {

                var this_checked_index = $(this).closest('tr').index(); //取得使用者勾選哪一個checkbox tr index
                var str_hfval=$('#<%=HF_cbindex.ClientID%>').val(); //取得使用者上一次點了哪一個CheckBox

                //當使用者按下shift 且 大於0(不包含GridView table head中CheckBox)
                if (shiftkeydown == true && this_checked_index > 0) {
                    var before_index;
                    if (str_hfval != '') {
                        var re = /^[0-9]+$/;
                        if (!re.test(str_hfval)) {
                            return false; //不是數字就中斷
                        };
                        before_index = parseInt(str_hfval);
                        if (before_index < this_checked_index) {
                            for (var i = before_index; i <= this_checked_index; i++) {
                                $('#<%=GridView1.ClientID %>').find('tr:eq(' + i + ') input[type="checkbox"]').prop('checked', true);
                            };
                        } else if (before_index > this_checked_index) {
                            for (var i = this_checked_index; i <= before_index; i++) {
                                $('#<%=GridView1.ClientID %>').find('tr:eq(' + i + ') input[type="checkbox"]').prop('checked', true);
                            };
                        };
                    };
                };

                if (this_checked_index > 0){
                    $('#<%=HF_cbindex.ClientID%>').val(this_checked_index); //紀錄目前使用者勾選哪一個checkbox tr index
                };
            });
        });
        function listening_for_shift(e) {
            if (e.keyCode == 16) {
                if (e.type === "keydown") {
                    //alert('down shift');
                    shiftkeydown = true;
                }
                if (e.type === "keyup") {
                    //alert('no shift');
                    shiftkeydown = false;
                }
            }
        };
    </script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <div class="container">
        <div class="row cust_blank1">
            <div class="col-xs-12">
                仿電子信箱中的CheckBox,勾選了第1筆之後,Shift鍵+滑鼠左鍵勾選第2筆,可以勾選我們要的多筆資料
                <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
                    <Columns>
                        <asp:TemplateField HeaderText="">
                            <HeaderTemplate>
                                <asp:CheckBox ID="GV1_headcb" runat="server" CssClass="rowcb GV1_headcb"/>
                            </HeaderTemplate>
                            <ItemTemplate>
                                <asp:CheckBox ID="CheckBox1" runat="server" CssClass="rowcb" />
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="編號">
                            <ItemTemplate>
                                <%# Eval("ser_no")%>
                                <asp:HiddenField ID="HF_serno" runat="server" Value='<%# Eval("ser_no")%>' />
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="標題">
                            <ItemTemplate>
                                <%# Eval("title")%>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="內容">
                            <ItemTemplate>
                                <%# Eval("cont")%>
                            </ItemTemplate>
                        </asp:TemplateField>
                    </Columns>
                    <EmptyDataTemplate>
                        <asp:Label ID="GV_Empty" runat="server" Text="目前無資料"></asp:Label>
                    </EmptyDataTemplate>
                </asp:GridView>
                <asp:HiddenField ID="HF_cbindex" runat="server" />
            </div>
        </div>
    </div>
</asp:Content>

說明:重點要記錄使用者第1次點了哪一個CheckBox,當使用者點了第2次時,就可以知道使用者點的範圍到哪裡,來作連續勾選的動作。

以上如有錯誤請指證,謝謝。

ASP.NET Table中的刪除按鈕,顯示2次提示訊息

以往要在GridView要刪除資料時,都會使用javascript confirm來問使用者是否確定要刪除資料,這次的需求是要跟使用者確認兩次,第一次詢問使用者「您確定要刪除這筆資料嗎?刪除後將無法還原!」,當按下確定後,第二次再次詢問使用者「真的確定要刪除這筆資料?」,按下確定刪除後,就真的刪除資料,所以這次要使用的是bootstrap dialog。

Step 1.首先到這個GitHub下載bootstrap3-dialog套件

Step 2.我會使用到jquery-1.11.3.js檔,也請先準備好,並且將下載好的dialog放到專案中。
說明:我的css目錄中有一個檔案名為bootstrap-dialog_vc.css,它的作用主要是讓訊息框可以垂直置中(請點我),因為預設dialog是不會幫我們作垂直置中的。
bootstrap-dialog_vc.css檔,內容如下:
.modal {
    text-align: center;
    padding: 0 !important;
}
.modal:before {
    content: '';
    display: inline-block;
    height: 100%;
    vertical-align: middle;
    margin-right: -4px;
}
.modal-dialog {
    display: inline-block;
    text-align: left;
    vertical-align: middle;
}

Step 3.我在寫ASP.NET WebForm的時候,都會習慣使用Master Page,所以請建立一個Master Page,名稱使用預設的名稱即可或自行定義也可以,程式碼如下:
<%@ 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_gridview_dialog</title>
    <link rel="stylesheet" href="css/bootstrap.min.css" />
    <script src="js/jquery-1.11.3.js"></script>
    <script src="js/bootstrap.min.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">
        /*MasterPage1 css*/
        body {
        background-color:#ffffff; /*背景顏色*/
        }
    </style>
    <script type="text/javascript">
        //MasterPage1 JavaScript
    </script>
  </head>
  <body>
    <form id="form1" runat="server">
        <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">
     
        </asp:ContentPlaceHolder>
    </form>
  </body>
</html>

Step 5.建立資料表,待會測試要用,以下是sql指令,方便讓大家建立資料表
以下為CREATE TABLE指令:
USE [test1]--這裡改成自己測試的資料庫
GO
/****** Object:  Table [dbo].[test_table1]    Script Date: 06/25/2017 19:16:26 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[test_table1](
[ser_no] [char](4) NOT NULL,
[title] [varchar](10) NULL,
[cont] [varchar](10) NULL,
 CONSTRAINT [PK_test_table1] PRIMARY KEY CLUSTERED
(
[ser_no] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO

以下為INSERT指令:
INSERT [dbo].[test_table1] ([ser_no], [title], [cont]) VALUES (N'0001', N'標題01', N'內容01')
INSERT [dbo].[test_table1] ([ser_no], [title], [cont]) VALUES (N'0002', N'標題02', N'內容02')
INSERT [dbo].[test_table1] ([ser_no], [title], [cont]) VALUES (N'0003', N'標題03', N'內容03')
INSERT [dbo].[test_table1] ([ser_no], [title], [cont]) VALUES (N'0004', N'標題04', N'內容04')
INSERT [dbo].[test_table1] ([ser_no], [title], [cont]) VALUES (N'0005', N'標題05', N'內容05')

Step 4.再來建立名為test_gv_dialog.aspx的網頁,程式碼如下:
前端:
<%@ Page Title="" Language="vb" AutoEventWireup="false" MasterPageFile="~/MasterPage1.master" CodeBehind="test_gv_dialog.aspx.vb" Inherits="test_gridview_dialog.test_gv_dialog" %>
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
    <link rel="stylesheet" href="css/bootstrap-dialog.min.css" />
    <link rel="stylesheet" href="css/bootstrap-dialog_vc.css" />
    <script src="js/bootstrap-dialog.min.js"></script>
    <style type="text/css">
        /*test_gv_dialog.aspx 只要是要使用在這個網頁的css都寫在此處*/
    </style>
    <script type="text/javascript">
        //test_gv_dialog.aspx JavaScript只要是要使用在這個網頁的JavaScript都寫在此處
        function delmessbox(int_row,str_mess) {
            BootstrapDialog.show({
                title: '確認是否刪除',
                message: '您確定要刪除「編號' + str_mess + '」這筆資料嗎?<br/>刪除後將無法還原!',
                buttons: [{
                    label: '確定',
                    action: function (dialog) {
                        delmessbox_again1(int_row, str_mess);
                        dialog.close();
                    }
                }, {
                    label: '取消',
                    action: function (dialog) {
                        dialog.close();
                    }
                }]
            });
            //因為使用bootstrap-dialog關係,當使用者按下按鈕後,呼叫delmessbox方法,會執行BootstrapDialog.show,
            //但必須要注意,程式它並不會停在BootstrapDialog.show,等待使用者回傳哪一個動作(確認或取消),
            //程式還是會繼續往下執行,如果沒有回傳false則會繼續執行Bdel按鈕中的OnClick事件中的下一個function,一樣會執行刪除。
            return false;
        };
        function delmessbox_again1(int_row, str_mess) {
            BootstrapDialog.show({
                title: '再次確認',
                message: '真的確定要刪除「編號' + str_mess + '」這筆資料?',
                buttons: [{
                    label: '確定刪除',
                    action: function (dialog) {
                        dialog.close();
                        con_exedel(int_row);
                    }
                }, {
                    label: '取消刪除',
                    action: function (dialog) {
                        dialog.close();
                    }
                }]
            });
        };
        function con_exedel(int_row) {
            //這裡將會把GridView所有的Button該呼叫哪一個function寫在這裡
            switch (int_row) {
                <%=str_deljs.ToString()%>
                <% str_deljs.Clear() : str_deljs = Nothing%>
            };
        };
    </script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <div class="container">
        <div class="row cust_blank1">
            <div class="col-xs-12">
                <span id="span_mess" runat="server"></span>
                <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">
                    <Columns>
                        <asp:TemplateField HeaderText="">
                            <ItemTemplate>
                                <asp:Button ID="Bdel" runat="server" Text="刪除" CommandName="de" CssClass="btn btn-default"/>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="編號">
                            <ItemTemplate>
                                <%# Eval("ser_no")%>
                                <asp:HiddenField ID="HF_serno" runat="server" Value='<%# Eval("ser_no")%>' />
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="標題">
                            <ItemTemplate>
                                <%# Eval("title")%>
                            </ItemTemplate>
                        </asp:TemplateField>
                        <asp:TemplateField HeaderText="內容">
                            <ItemTemplate>
                                <%# Eval("cont")%>
                            </ItemTemplate>
                        </asp:TemplateField>
                    </Columns>
                    <EmptyDataTemplate>
                        <asp:Label ID="GV_Empty" runat="server" Text="目前無資料"></asp:Label>
                    </EmptyDataTemplate>
                </asp:GridView>
            </div>
        </div>
    </div>
</asp:Content>

後端:
Imports System.Data.SqlClient

Public Class test_gv_dialog
    Inherits System.Web.UI.Page
    Dim str_sqlconn As String = "Server=127.0.0.1;uid=test;pwd=test;Database=test1" '可以寫在web.config
    Dim str_sql As String = String.Empty
    Dim def_dt As DataTable = Nothing
    Dim int_gv_row As Integer = 0
    Public str_deljs As StringBuilder = Nothing
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not Page.IsPostBack Then
            str_sql = "select * from test_table1"
            def_dt = queryDataTable2(str_sql)
            GridView1.DataSource = def_dt
            GridView1.DataBind()
            GridView1.CssClass = "table table-bordered"
            def_dt.Clear() : def_dt.Dispose() : def_dt = Nothing
        End If
    End Sub

    Function queryDataTable2(ByVal sql_str As String, Optional ByVal pv As String = Nothing, Optional ByVal sqlvalue_p() As String = Nothing) As DataTable
        Dim sql_p() As String = Nothing
        If Not pv Is Nothing Then
            sql_p = pv.Split(",") 'sql字串中的參數
        End If

        Dim ds As New DataSet()
        Using conn As New SqlConnection(str_sqlconn)
            Dim command As SqlCommand = New SqlCommand(sql_str, conn)

            If Not pv Is Nothing And Not sqlvalue_p Is Nothing Then
                Dim x_end As Integer = sql_p.Length - 1
                For x As Integer = 0 To x_end
                    If command.Parameters.Contains(sql_p(x)) Then
                        command.Parameters(sql_p(x)).Value = sqlvalue_p(x)
                    Else
                        command.Parameters.AddWithValue(sql_p(x), sqlvalue_p(x)) '讓ADO.NET自行判斷型別轉換
                    End If
                Next
            End If

            Dim da As New SqlDataAdapter()
            da.SelectCommand = command

            da.Fill(ds)

            command.Cancel()
            command.Dispose()
        End Using
        If ds.Tables.Count > 0 Then
            Return ds.Tables(0)
        Else
            Return New DataTable()
        End If
    End Function

    Private Sub GridView1_RowDataBound(sender As Object, e As GridViewRowEventArgs) Handles GridView1.RowDataBound
        If e.Row.RowType = DataControlRowType.Header Then
            '設定欄位Class
            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-1 col-md-1"
            e.Row.Cells(3).CssClass = "col-xs-1 col-md-1"

            str_deljs = New StringBuilder()
        End If
        If e.Row.RowType = DataControlRowType.DataRow Then
            Dim Bdel As Button = CType(e.Row.FindControl("Bdel"), Button)
            If Not (Bdel Is Nothing) Then
                '在Bdel按鈕中,加入onclick事件delmessbox方法
                Bdel.Attributes.Add("onclick", "javascript:return delmessbox(" & int_gv_row & ",'" & CType(e.Row.DataItem, DataRowView).Item("ser_no").ToString() & "');" & ClientScript.GetPostBackEventReference(Bdel, "").ToString() & ";")
            End If
            str_deljs.Append("case " & int_gv_row & ":" & ClientScript.GetPostBackEventReference(Bdel, "").ToString() & ";break;")
            int_gv_row = int_gv_row + 1
        End If
    End Sub

    Private Sub GridView1_RowCommand(sender As Object, e As GridViewCommandEventArgs) Handles GridView1.RowCommand
        If e.CommandName = "de" 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就是您所點選的某一列

            str_sql = "delete test_table1 where ser_no=@ser_no"
            '建議HF_serno的Value要事先先加密,當使用按下按鈕之後,再解密這樣會比較安全
            Dim str_serno As String = CType(GridView1.Rows(GV_Row.RowIndex).FindControl("HF_serno"), HiddenField).Value
            Dim int_enq As Integer = 0
            Using conn As New SqlConnection(str_sqlconn)
                conn.Open()

                Dim command As SqlCommand = New SqlCommand(str_sql, conn)

                If command.Parameters.Contains("@ser_no") Then
                    command.Parameters("@ser_no").Value = str_serno
                Else
                    command.Parameters.AddWithValue("@ser_no", str_serno) '讓ADO.NET自行判斷型別轉換
                End If

                int_enq = command.ExecuteNonQuery()

                command.Cancel() 'SqlDataReader要close前,要先Cancel SqlCommand
                command.Dispose()

                conn.Close()
            End Using
            If int_enq = 1 Then
                Me.span_mess.InnerHtml = "<span style=" & Chr(34) & "color:blue;" & Chr(34) & ">系統於 " & Date.Now.ToString("yyyy/MM/dd HH:mm:ss") & " 已刪「編號:" & str_serno & "」這筆資料。</span>"
                '刪除成功,重新讀取資料
                str_sql = "select * from test_table1"
                def_dt = queryDataTable2(str_sql)
                GridView1.DataSource = def_dt
                GridView1.DataBind()
                GridView1.CssClass = "table table-bordered"
            Else
                Me.span_mess.InnerHtml = "<span style=" & Chr(34) & "color:red;" & Chr(34) & ">系統於 " & Date.Now.ToString("yyyy/MM/dd HH:mm:ss") & " 「編號:" & str_serno & "」刪除失敗。</span>"
            End If
        End If
    End Sub
End Class

執行結果:


以上,說明我寫在註解中,如有錯誤的地方,請指證,謝謝。

2017年6月14日 星期三

ASP.NET input button使用javascript觸發postback

不使用ASP.NET WebForm Button控制項,使用html Button並在Button的onclick事件中,使用javascript觸發postback,在Page_Load中去確認哪一個按鈕觸發。

以下的作法,不曉得是否正確,還請各位程式設計師們不吝指教。

test_js_postback.aspx
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="test_js_postback.aspx.vb" Inherits="test_js_postback" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title></title>
    <script type="text/javascript">
        function Button1_Click() {
            <%=ClientScript.GetPostBackEventReference(Me, "Button1_Click").ToString%>
        };
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <input id="Button1" type="button" value="測試javascript觸發postback" onclick="Button1_Click();" />
        <span id="span_message" runat="server">[訊息顯示在此處]</span>
    </div>
    </form>
</body>
</html>


test_js_postback.aspx.vb
Partial Class test_js_postback
    Inherits System.Web.UI.Page

    Private Sub test_js_postback_Load(sender As Object, e As EventArgs) Handles Me.Load
        If Not Page.IsPostBack Then
        Else
            Dim con As Control = Nothing
            Dim str_event_target As String = String.Empty
            Dim str_event_targument As String = String.Empty
            str_event_target = Page.Request.Params.Get("__EVENTTARGET") 'html hidden __EVENTTARGET
            str_event_targument = Page.Request.Params.Get("__EVENTARGUMENT") 'html hidden __EVENTARGUMENT

            If (Not str_event_target Is Nothing) And (str_event_target <> "") Then
                con = Page.FindControl(str_event_target)
                Response.Write("EVENTTARGET=[" & str_event_target & "][" & con.GetType.ToString & "]<br/>")
                If InStr(1, str_event_target.ToLower, "page", CompareMethod.Text) > 0 Then
                    If (Not str_event_targument Is Nothing) And (str_event_targument <> "") Then
                        Response.Write("EVENTARGUMENT=[" & str_event_targument & "]<br/>")
                        If str_event_targument = "Button1_Click" Then 'html input的Button1_Click
                            Me.span_message.InnerHtml = "觸發Button1_Click<br/>"
                        End If
                    Else
                        Response.Write("EVENTARGUMENT=[無]<br/>")
                    End If
                End If

            Else
                Response.Write("PostBack:可能是某個控制項送出<br/>")
            End If

        End If
    End Sub
End Class

2017年6月12日 星期一

CSS



20170613
改變checkbox大小(參考:Check box size change with CSS [duplicate])
input[type="checkbox"]{
width: 20px; /*Desired width*/
height: 20px; /*Desired height*/
}