asp.netcheckboxserver-sideclient-side

When asp.net CheckBox control is changed, how to fire client side AND server side events


I have an asp.net Checkbox inside a gridview. It has some javascript in OnClick to confirm that the operator wishes to proceed. It also has some server side code on the OnCheckedChanged event.

The client side code is being executed, but the server side code is not being triggered, irrespective of whether the operator elects to proceed or not. I have confirmed this by adding some breakpoints.

If I remove the OnClick client side code, the server side code is executed correctly.

Any suggestions on what I'm doing wrong would be very much appreciated.

Code below:

 <asp:CheckBox ID="InclSectionCB" OnClick="return ProceedYN();" OnCheckedChanged="InclSectionCBChanged" runat="server" AutoPostBack="True" />
    <script type="text/javascript">
        function ProceedYN() {
            return confirm('Proceed?');
        }
    </script>

I expected that upon checking/unchecking the checkBox that the steps would be:

  1. Client side javascript prompts the operator if they would like to proceed. 2a) If operator selects 'Ok', then Checkbox would be changed and the server side code in InclSectionCBChanged would be executed. 2b) If operator selects 'Cancel', then Checkbox would not be changed and the server side code in InclSectionCBChanged would not be executed.

What is happening is:

  1. Client side javascript prompts the operator if they would like to proceed. (correct) 2a) If operator selects 'Ok', then Checkbox is changed but the server side code in InclSectionCBChanged is not executed. 2b) If operator selects 'Cancel', then Checkbox is not changed and the server side code in InclSectionCBChanged is not executed.

Solution

  • While we commonly code 2 click events for a button (client side, and server side) click event, in the case of a check box?

    It turns out there is only one click event, and thus the asp.net system has no choice but to combine BOTH click events into a one common routine. (client side + server-side events are combined into one event in the JavaScript).

    This means is that your first code (the client-side JavaScript click) has to work somewhat differently.

    If user hits cancel, then the calling code (note carefully here, I stated the CALLING code!!!) has to stop, and return false and exit the code stub.

    However, if the user hits OK, then we have to let the calling code CONTINUE and then run the server side click event.

    You can see this effect by inspecting the JavaScript generated.

    If I have a check box like this:

            <asp:CheckBox ID="CheckBox1" runat="server"
                AutoPostBack="true"
                OnCheckedChanged="CheckBox1_CheckedChanged"
                onclick="return myjsclick(this)"
                />
    

    As noted, asp.net will combine BOTH the OnCheckedChanged code and the onclick code into one JavaScript chunk of code (there are not really 2 separate events).

    Above gets converted into:

    <input id="CheckBox1" 
       type="checkbox" 
       name="CheckBox1" 
    onclick="return myjsclick(this);setTimeout('__doPostBack(\'CheckBox1\',\'\')', 0)">
    

    So, just remember that your onclick (client side) code will be combined with the server-side expression (the __doPostBack to run/trigger the server-side code behind).

    However, in above since we return myjsclick() always, then the above bit of code will EXIT and NEVER continue to run and call/run the server-side code.

    So, we have to re-write our client side on click routine to CONTINUE to run code that follows AFTER the expression, but only do so when the user confirms the dialog.

    That means if the user cancels, then we EXIT the code, but if the user hits confirm, then we continue to run the code that will/would/does follow our onclick code.

    So, we can write the code this way:

            <asp:CheckBox ID="CheckBox1" runat="server"
                AutoPostBack="true"
                OnCheckedChanged="CheckBox1_CheckedChanged"
                onclick="if (!myjsclick(this)) {return false};"
                />
    

    So, if the expression is false, then we return false (and exit the code). However, if you hit ok, then the {return false} will NOT run, but ANY code that follows the above expression will continue to run.

    The above when compiled turns into this markup:

    <input id="CheckBox1" 
       type="checkbox" 
        name="CheckBox1" 
    
     onclick="if (!myjsclick(this)) {return false};setTimeout('__doPostBack(\'CheckBox1\',\'\')', 0)">
    

    So, now as you can see, if user hits cancel, then the above JavaScript code will exit, but if you hit OK, then the lines of code that FOLLOWS the expression will run. As noted, both client side, and server-side events are combined into a single bit of JavaScript code, and thus we have to change how such "confirm" code is written.

    I should point out that often I use the above when wanting to have a button with an icon, and thus I use a standard button, and NOT a asp.net one. They work much the same, but you AGAIN have to use the above trick and "assume" that your click event, and the server event are combined into one single bit of JavaScript code.

    So, doing above with a check box, or a standard button (with runat=server), we have this:

    <button id="cmdViewPeople" runat="server" type="button"
        onserverclick="cmdViewPeople_ServerClick"
        onclick="if !(myconfirm(this) {return false};"
        class="btn myshadow">
        <span class="fa fa-bed fa-lg"> Booking Details</span>
    </button>
    

    The above button looks like this:

    enter image description here

    So, the ONLY reason to use a "button" as opposed to a asp.net one was to have that icon (font awesome) in that button. But note how the button has both a client side click, and a server side. Just remember the SAME rule as in regards to the check box applies to the button example.

    So, let's put this all together, and do this in a grid view.

    We will conditionally run the check box, and then in code behind take action on the given GridView check box row click.

    So, we now have this simple markup:

    <h3><i>Hotels and Bookings</i></h3>
    <asp:GridView ID="GVHotels" runat="server" AutoGenerateColumns="False"
        DataKeyNames="ID" CssClass="table table-hover"
        Width="50%">
        <Columns>
            <asp:BoundField DataField="FirstName" HeaderText="FirstName" />
            <asp:BoundField DataField="LastName" HeaderText="LastName" />
            <asp:BoundField DataField="City" HeaderText="City" />
            <asp:BoundField DataField="HotelName" HeaderText="Hotel" />
            <asp:BoundField DataField="Description" HeaderText="Descriptioin" />
            <asp:TemplateField HeaderText="Set Hotel Active"
                ItemStyle-HorizontalAlign="Center" ItemStyle-Width="130px">
                <ItemTemplate>
                    <asp:CheckBox ID="CheckBox1" runat="server"
                        AutoPostBack="true"
                        OnCheckedChanged="CheckBox1_CheckedChanged"
                        onclick="if (!myjsclick(this)) {return false};" />
    
                </ItemTemplate>
            </asp:TemplateField>
        </Columns>
    </asp:GridView>
    
    
    <script>
    
        function myjsclick() {
    
            return confirm("Activating this hotel can cost money\nPlease confirm")
        }
    
    </script>
    

    And code to load the grid is this:

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    
        If Not IsPostBack Then
            LoadGrid
        End If
    
    End Sub
    
    Sub LoadGrid()
    
        GVHotels.DataSource = MyRst("SELECT * FROM tblHotelsA
                                    ORDER BY HotelName")
        GVHotels.DataBind()
    
    End Sub
    

    And code behind for the check box. Note VERY closely how the server-side check box code can pick up the GridView row by use of NamingContainer. Note that NamingContainer is rather handy, since then we dispense with the specialized GridView row commands. Use of NamingContainer works for check box, combo box, or even just a simple button click in a GridView to get the current GridView row information.

    So that code looks like this:

    Protected Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs)
    
        Dim chk As CheckBox = sender
        Dim gRow As GridViewRow = chk.NamingContainer
    
        Debug.Print($"Row index click = {gRow.RowIndex}")
        ' get hidden non exposed database PK value
        Dim intPK As Integer = GVHotels.DataKeys(gRow.RowIndex)("ID")
    
        Debug.Print($"Database PK value = {intPK}")
        Debug.Print($"Check box checked value = {chk.Checked}")
    
        ' code here to do whatever.
    
        Dim cmdSQL As New SqlCommand(
            "UPDATE tblHotels SET Active = @Active
            WHERE ID = @ID")
    
        cmdSQL.Parameters.Add("@ID", SqlDbType.Int).Value = intPK
        cmdSQL.Parameters.Add("@Active", SqlDbType.Bit).Value = chk.Checked
        MyRstPE(cmdSQL)
    
    End Sub
    

    So, running we get this effect:

    enter image description here

    And in the server-side Immediate window, we see this:

    Row index click = 0
    Database PK value = 17
    Check box checked value = True
    

    So, row click index, data base key, or even any value from that row click can be had with the above code.