Friday, August 16, 2013

Custom .NET Validator for CheckBoxList control

Here is a User Control that serves as a custom validator for a .NET CheckBoxList control.

ASCX file:

  1. <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CheckBoxListCustomValidator.ascx.cs" Inherits="MARE.Directory.UserControls.CheckBoxListCustomValidator" ClientIDMode="AutoID" %>
  2. <asp:CustomValidator ID="valCheckBoxList" class="error" EnableClientScript="true" Text="Required" ErrorMessage="Required" runat="server" />
  3.  
  4. <script type="text/javascript">
  5.     (function ($) {
  6.  
  7.         <%= ClientValidationMethodName %> = function (sender, e) {
  8.             var $checkBoxList = $('#<%= CheckBoxListToValidate.ClientID %>');
  9.             e.IsValid = $checkBoxList.find(':checkbox:checked').length > 0;
  10.         };
  11.  
  12.         $(document).ready(function () {
  13.             var $checkBoxList = $('#<%= CheckBoxListToValidate.ClientID %>');
  14.             // setup validation
  15.             $checkBoxList.find(':checkbox').click(function () {
  16.                 // .NET validate the checkbox list
  17.                 var validator = document.getElementById('<%= valCheckBoxList.ClientID %>');
  18.                 ValidatorValidate(validator);
  19.             });
  20.         });
  21.  
  22.     })(jQuery);
  23. </script>

Code-behind:

  1. using System;
  2. using System.Web.UI;
  3. using System.Web.UI.WebControls;
  4.  
  5. namespace MARE.Directory.UserControls
  6. {
  7.     public partial class CheckBoxListCustomValidator : System.Web.UI.UserControl
  8.     {
  9.         private CheckBoxList _checkBoxListToValidate;
  10.  
  11.         protected void Page_Load(object sender, EventArgs e)
  12.         {
  13.             if (!string.IsNullOrEmpty(ControlToValidate))
  14.             {
  15.                 _checkBoxListToValidate = FindCheckBoxListRecursive(this.Page, ControlToValidate);
  16.             }
  17.             valCheckBoxList.ClientValidationFunction = ClientValidationMethodName;
  18.             valCheckBoxList.ServerValidate += ValidateCheckBoxList;
  19.         }
  20.  
  21.         private string _clientValidationMethodName;
  22.         public string ClientValidationMethodName
  23.         {
  24.             get
  25.             {
  26.                 if (_clientValidationMethodName == null)
  27.                 {
  28.                     _clientValidationMethodName = string.Format("window.val{0}", Guid.NewGuid().ToString("N"));
  29.                 }
  30.                 return _clientValidationMethodName;
  31.             }
  32.         }
  33.  
  34.         public string ControlToValidate { get; set; }
  35.  
  36.         public CheckBoxList CheckBoxListToValidate { get { return _checkBoxListToValidate; } }
  37.  
  38.         public CustomValidator CheckBoxListValidator { get { return valCheckBoxList; } }
  39.  
  40.         public string ValidatorText
  41.         {
  42.             get { return CheckBoxListValidator.Text; }
  43.             set { CheckBoxListValidator.Text = value; }
  44.         }
  45.  
  46.         public string ValidatorErrorMessage
  47.         {
  48.             get { return CheckBoxListValidator.ErrorMessage; }
  49.             set { CheckBoxListValidator.ErrorMessage = value; }
  50.         }
  51.  
  52.         public void SetCheckBoxListToValidate(CheckBoxList checkBoxListToValidate)
  53.         {
  54.             _checkBoxListToValidate = checkBoxListToValidate;
  55.         }
  56.  
  57.         internal void ValidateCheckBoxList(object sender, ServerValidateEventArgs e)
  58.         {
  59.             bool isValid = false;
  60.             int i = 0;
  61.  
  62.             while (i < _checkBoxListToValidate.Items.Count && !isValid)
  63.             {
  64.                 ListItem li = _checkBoxListToValidate.Items[i];
  65.                 isValid = li.Selected;
  66.                 i++;
  67.             }
  68.  
  69.             e.IsValid = isValid;
  70.         }
  71.  
  72.         private static CheckBoxList FindCheckBoxListRecursive(Control control, string id)
  73.         {
  74.             if (control == null) return null;
  75.             //try to find the control at the current level
  76.             CheckBoxList ctrl = control.FindControl(id) as CheckBoxList;
  77.  
  78.             if (ctrl == null)
  79.             {
  80.                 //search the children
  81.                 foreach (Control child in control.Controls)
  82.                 {
  83.                     ctrl = FindCheckBoxListRecursive(child, id);
  84.  
  85.                     if (ctrl != null) break;
  86.                 }
  87.             }
  88.             return ctrl;
  89.         }
  90.     }
  91. }

Implementation:

  1. <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MyUserControl.ascx.cs" Inherits="MARE.Directory.UserControls.MyUserControl" %>
  2. <%@ Register Src="../UserControls/CheckBoxListCustomValidator.ascx" TagPrefix="uc1" TagName="CheckBoxListCustomValidator" %>
  3.  
  4. <asp:CheckBoxList ID="cblMyCheckboxList1" RepeatDirection="Horizontal" RepeatColumns="4" runat='server' />        
  5. <uc1:CheckBoxListCustomValidator runat="server" id="valcblMyCheckboxList1" ControlToValidate="cblMyCheckboxList1" ValidatorErrorMessage="Required" />
  6.  
  7. <asp:CheckBoxList ID="cblMyCheckboxList2" RepeatDirection="Horizontal" RepeatColumns="4" runat='server' />        
  8. <uc1:CheckBoxListCustomValidator runat="server" id="valcblMyCheckboxList2" ControlToValidate="cblMyCheckboxList2" ValidatorErrorMessage="Required" />

Customize DotNetNuke 7 display for file upload controls

DNN7 enhances the file upload control (input type=”file”) with a button display that shows the name of the selected file.  Unfortunately, this control displays only a single file name even if multiple files are selected.  Internet Explorer 9 and earlier versions do not support multiple files but most other browsers do.

Rendered without the DNN7 enhancement:

<input type="file" multiple="multiple" name="dnn$ctr618$Dispatch$myControl$myUpload" id="myUpload">

Rendered with the DNN7 enhancement:

<span class="dnnInputFileWrapper">
    <span class="dnnSecondaryAction">Choose File</span>
    <input type="file" multiple="multiple" name="dnn$ctr618$Dispatch$myControl$myUpload" id="myUpload">
</span>

The method that enhances file upload controls is declared in dnn.jquery.js and is called $.fn.dnnFileInput.  This method can be replaced with a custom method to display multiple file names.  My customization is in the declaration of the $ctrl.change event handler.

  1. (function ($) {
  2.     "use strict";
  3.  
  4.     function dnnFileInput() {
  5.         return this.each(function () {
  6.             var $ctrl = $(this),
  7.                 text = '',
  8.                 btn = {};
  9.  
  10.             if (this.wrapper)
  11.                 return;
  12.  
  13.             //if this.wrapper is undefined, then we check if parent node is a wrapper
  14.             if (this.parentNode && this.parentNode.tagName.toLowerCase() === 'span' && this.parentNode.className === 'dnnInputFileWrapper') {
  15.                 return;
  16.             }
  17.  
  18.             this.wrapper = $("<span class='dnnInputFileWrapper'></span>");
  19.             $ctrl.wrap(this.wrapper);
  20.             text = $ctrl.data('text');
  21.             text = text || 'Choose File';
  22.             btn = $("<span class='dnnSecondaryAction'>" + text + "</span>");
  23.             btn.insertBefore($ctrl);
  24.  
  25.             // display the full list of uploaded files
  26.             $ctrl.change(function () {
  27.                 var val = $(this).val(),
  28.                     files = [],
  29.                     i = 0,
  30.                     max = 0,
  31.                     fName = '',
  32.                     lastIdx = 0;
  33.  
  34.                 if (val !== '') {
  35.                     if (!this.files) {
  36.                         files = [{ name: /([^\\]+)$/.exec(this.value)[1] }];
  37.                     }
  38.                     else {
  39.                         files = this.files;
  40.                     }
  41.                     max = files.length;
  42.                     for (i = 0; i < max; i += 1) {
  43.                         fName += files[i].name + ', ';
  44.                     }
  45.                     lastIdx = fName.lastIndexOf(',');
  46.                     val = fName.substring(0, lastIdx);
  47.                 } else {
  48.                     val = text;
  49.                 }
  50.                 $(this).prev().html(val);
  51.             });
  52.         });
  53.     };
  54.  
  55.     $(document).ready(function () {
  56.         $.fn.dnnFileInput = dnnFileInput;
  57.     });
  58.  
  59. }(jQuery));

I call the above function from an external script file that is loaded in the page head.

Thanks to the following references which helped me display the uploaded file name in IE9 and earlier: