Saturday, June 7, 2014

Get the "check all" checkbox working across all pages of pagination in a DevExpress ASPxGridView, understand the distinction between setTimeout and setInterval in JavaScript and pass a variable to a function called by setTimeout. Bonus: 32 is the spacebar keycode.

Alright, I have an ASPxGridView which starts out as seen below in ASP.NET markup. It is preceded by a hidden form field which I shall use as a backing store for state, specifically the state for if the "check all" checkbox should be checked or not. The string "true" or the string "false" will live inside the hidden form field. A trick like this is needed, as otherwise when one changes pages within the ASPxGridView's pagination, the "check all" checkbox will fall back into being unchecked.

<input type="hidden" value="false" ID="CheckAllCheckBoxStateManagement"
      runat="server" ClientIDMode="Static"/>
<dx:ASPxGridView AutoGenerateColumns="False" ID="MyGrid" ClientIDMode="Static"
      runat="server" KeyFieldName="FooId">
   <ClientSideEvents EndCallback="EndCallbackAct" />
   <Columns>
      <dx:GridViewCommandColumn ShowSelectCheckbox="true" Name="CheckAll"
            Width="5%" AllowDragDrop="False" VisibleIndex="0">
         <HeaderTemplate>
            <dx:ASPxCheckBox ID="CheckAllCheckBox" ClientIDMode="Static"
                  type="checkbox" runat="server" ToolTip="toggle me"
                  CssClass="TooltipCss">
               <ClientSideEvents CheckedChanged="CheckedChangedAct">
               </ClientSideEvents>
            </dx:ASPxCheckBox>
         </HeaderTemplate>
      </dx:GridViewCommandColumn>

 
 

Alright, clearly there are two client-side (JavaScript) events being defined here. I'll get to those in a moment, but it is also important to note that there is at least one server-side (C#) event in play too. It is wired up like so in C#:

MyGrid.CustomCallback += MyGrid_CustomCallback;

 
 

It has a signature like this and it will, by way of its code, either select all rows across all pages in the ASPxGridView's pagination (which do not have hidden checkboxes) or unselect all the rows. I have a different blog posting which details how this works.

private void MyGrid_CustomCallback(object sender,
      ASPxGridViewCustomCallbackEventArgs e)
{

 
 

...it is important to note that the MyGrid_CustomCallback server-side event will set off the EndCallbackAct client-side event when it ends which is defined in JavaScript like so:

function EndCallbackAct(s, e) {
   var isChecked = $('#CheckAllCheckBoxStateManagement').val();
   if (isChecked == 'true') {
      CheckAllCheckBox.SetChecked(true);
   } else {
      CheckAllCheckBox.SetChecked(false);
   }
}

 
 

EndCallbackAct is clearly reading from the hidden form field, but what writes to the hidden form field? The answer is the client-side event wired up to the "check all" checkbox, namely:

function CheckedChangedAct(s, e) {
   var isChecked = s.GetChecked();
   $('#CheckAllCheckBoxStateManagement').val(isChecked);
   MyGrid.PerformCallback(isChecked);
}

 
 

CheckedChangedAct, in its last line, kicks off MyGrid_CustomCallback, which either selects all selectable rows or unselects everything and then calls EndCallbackAct. That is the majority of the work to be done. The only outlier I saw was that if I unchecked a checkbox for a row in the ASPxGridView, it seemed to me like the "check all" checkbox should ALSO get unchecked so that it is no longer in an "everything is checked" state. Let's discuss how that may be facilitated. It is important to note that the HTML for the "check all" check box is going to look something like this:

<span class=" dxICheckBox_PortalDefault dxWeb_edtCheckBoxChecked_PortalDefault"
      id="CheckAllCheckBox_S_D">
   <span class="dxKBSW">
      <input id="CheckAllCheckBox_S" name="dnn$ctr2195$ViewWorkFlow
            $WorkFlowUC1$wfEnvironment$WorkFlowNav$GCTC2$TC
            $EnvironmentTemplateControls_8$certCallback$MyGrid$header0$TC$
            CheckAllCheckBox" value="U" type="text" readonly="readonly"
            style="opacity:0;width:1px;height:1px;position:relative;
            background-color:transparent;display:block;margin:0;padding:0;
            border-width:0;font-size:0pt;">
   </span>
</span>

 
 

The HTML for a checkbox for just selecting a row in the ASPxGridView will be as such. (Note that neither of these is really a checkbox in the traditional sense.)

<span class=" dxICheckBox_PortalDefault
      dxWeb_edtCheckBoxUnchecked_PortalDefault" id="MyGrid_DXSelBtn16_D">
   <span class="dxKBSW">
      <input id="MyGrid_DXSelBtn16" value="C" type="text" readonly="readonly"
            style="opacity:0;width:1px;height:1px;position:relative;
            background-color:transparent;
            display:block;margin:0;padding:0;border-width:0;
            font-size:0pt;">
   </span>
</span>

 
 

These two may vary in that they both may either have the dxWeb_edtCheckBoxChecked_PortalDefault class or the dxWeb_edtCheckBoxUnchecked_PortalDefault class depending on whether they appear as checked or not. Whenever I check one of the checkboxes, I check to see if it should uncheck the "check all" checkbox like so in JavaScript:

$(function() {
   $("input").live("focus", function () {
      var control = $(this);
      setTimeout(function () {
         UncheckCheckAllCheckboxIfApplicable(control);
      }, 200);
   });
   $("input").live("keydown", function () {
      var control = $(this);
      var key;
      if (window.event) {
         key = window.event.keyCode;
      } else {
         key = event.which;
      }
      if (key == 32) {
         setTimeout(function() {
            UncheckCheckAllCheckboxIfApplicable(control);
         }, 200);
      }
   });
});

 
 

Two acts do the same thing. A focus upon the control inside the "checkbox" HTML is one of the acts and the pressing of the spacebar to check or uncheck a "checkbox" is the other act. Both will delay for a fifth of a second before checking to see what class (dxWeb_edtCheckBoxChecked_PortalDefault or dxWeb_edtCheckBoxUnchecked_PortalDefault) is present at the "checkbox" giving other JavaScript mechanics time enough to change out the class if merited.

function UncheckCheckAllCheckboxIfApplicable(control) {
   control = control.parent().parent();
   var wrapper = control.parent().parent().parent().parent().parent().parent().parent().parent();
   if (control[0].className.indexOf("dxWeb_edtCheckBoxUnchecked_PortalDefault")
         >= 0) {
      if (control[0].id != "CheckAllCheckBox") {
         if (wrapper[0].id == "MyGrid") {
            if($('#CheckAllCheckBox').children(0)[0].className
                  .indexOf("dxWeb_edtCheckBoxChecked_PortalDefault") >= 0) {
               $('#CheckAllCheckBoxStateManagement').val('false');
               CheckAllCheckBox.SetChecked(false);
            }
         }
      }
   }
}

 
 

setTimeout unlike setInterval, is only going to run once. Hey, while we are discussing this, something else interesting that I learned how to do but never used was this:

ASPxCheckBox checkAllCheckBox =
      MyGrid.FindHeaderTemplateControl(MyGrid.Columns[0], "CheckAllCheckBox") as
      ASPxCheckBox;

 
 

Like so, one may latch onto the "check all" checkbox in C# to manipulate it.

No comments:

Post a Comment