Частичная проверка правильности ввода данных.

Довольно часто можно столкнуться с проблемой, когда на одной странице находятся две «формы», правильность данных в которых необходимо проверять по отдельности.
Обычно приходиться жертвовать клиентской частью проверок и делать все проверки на сервере, но не очень удобно.

Далее мы рассмотрим способы, которыми можно реализовать частичную проверку. Первый способ достаточно прост и подходит, когда у нас все проверяющие элементы управления (Валидаторы) находятся на одной странице. Ниже представлен пример того, как это реализуется.

<%@ Page %>
<%@ Import Namespace="System.Collections"%>
<head>
  <script language="javascript">
    function EnableValidationGroup( activeValidators ) {
      // Если на странице нет валидаторов то ничего не делаем
      if (typeof(Page_Validators)=='undefined') return;
  
      //Выключаем все валидаторы
      for( i = 0; i < Page_Validators.length; i++ )
        Page_Validators[i].enabled = false;
  
      //Включаем нужные нам валидаторы
      for( i = 0; i < activeValidators.length; i++ )
        activeValidators[i].enabled = true;
    }
  </script>
  <script runat="server" language="c#">
    Hashtable _validationGroups = new Hashtable();
  
    void CreateValidationGroup(string name, params BaseValidator[] validators) {
      _validationGroups[name] = validators;
      foreach(BaseValidator validator in validators) {
        RegisterArrayDeclaration(name,string.Format("document.all["{0}"]",validator.ClientID));  
      }
    }
    
    bool IsValidationGroupValid(string name) {
      BaseValidator[] validators = (BaseValidator[])_validationGroups[name];
      bool isValid = true;
      foreach(BaseValidator validator in validators) {
        isValid &= validator.IsValid;
      }
      return isValid;
    }
  
    void RegisterControlValidationGroup(Button button,string name) {
      button.Attributes.Add("onclick","EnableValidationGroup(" + name + ");");
    }
    void Page_Load(object sender, EventArgs e) {
      CreateValidationGroup("__validatorGroup1",_passwordRequired,_loginRequired);
      CreateValidationGroup("__validatorGroup2",_titleRequired,_messageRequired);
      
      RegisterControlValidationGroup(_loginButton,"__validatorGroup1");
      RegisterControlValidationGroup(_postButton,"__validatorGroup2");
    }
    
    void OnLoginButtonClick(object sender, EventArgs e) {
      if (!IsValidationGroupValid("__validatorGroup1")) {
        return;
      }
      Response.Write("Login sucessfull");
    }
    void OnPostButtonClick(object sender, EventArgs e) {
      if (!IsValidationGroupValid("__validatorGroup2")) {
        return;
      }
      Response.Write("Post sucessfull");
    }    
  </script>
</head>
<body>
  <form runat="server">
  <table width="100%">
    <tr>
      <td width="200px" valign="top">
        <fieldset>
          <legend>Login</legend>
          <table width="200">
            <tr>
              <td>Login:</td>
              <td>
                <asp:TextBox id="_loginText" runat="server" Width="150px"/>
                <asp:RequiredFieldValidator  ErrorMessage="Login is required field" 
                  runat="server" id="_loginRequired" Text="*" Display="None"
                  ControlToValidate="_loginText"/>
              </td>
            </tr>
            <tr>
              <td>Password:</td>
              <td><asp:TextBox id="_passwordText" TextMode="Password" 
                runat="server" Width="150px"/>
              <asp:RequiredFieldValidator ErrorMessage="Password is required field"
                runat="server" id="_passwordRequired" Text="*" 
                Display="None" ControlToValidate="_passwordText"/>
              </td>
            </tr>
            <tr>
              <td></td>
              <td><asp:Button runat="server" id="_loginButton" 
                Text="Log In" Width="100px" OnClick="OnLoginButtonClick"/></td>
            </tr>
          </table>
        </fieldset>
      </td>
      <td width="100%">
        <fieldset>
          <legend>Message Post</legend>
          <table>
            <tr>
              <td>Title:</td>
              <td><asp:TextBox id="_titleText" runat="server" Width="400px"/>
              <asp:RequiredFieldValidator ErrorMessage="Title is required field"
                runat="server" id="_titleRequired" Text="*" Display="None"
                ControlToValidate="_titleText"/>
              </td>
            </tr>
            <tr>
              <td colspan="2">Message:</td>
            </tr>
            <tr>
              <td colspan="2">
                <asp:TextBox  TextMode="MultiLine" Rows="20" id="_messageText" 
                  runat="server" Width="600px"/>
                <asp:RequiredFieldValidator runat="server" 
                  ErrorMessage="Message is required field" id="_messageRequired" Text="*"
                  Display="None" ControlToValidate="_messageText"/>
              </td>
            </tr>
            <tr>
              <td align="right" colspan="2"><asp:Button id="_postButton" 
                runat="server" Text="Post" Width="100px"
                OnClick="OnPostButtonClick"/></td>
            </tr>
          </table>
        </fieldset>  
      </td>
    </tr>
  </table>
    <asp:ValidationSummary runat="server" ShowMessageBox="true" ShowSummary="false"/>
  </form>
</body>

Но, в случае использования данного функционала на нескольких страницах, приходится делать Copy & Paste кода, что не очень красиво и муторно, тем более, что достаточно просто допустить ошибку. Для того чтобы избежать этого, мы инкапсулируем всю логику в отдельный элемент управления.

using System;
using System.IO;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
namespace TestWebControls
{
  [
    Designer("System.Web.UI.Design.WebControls.PanelDesigner, System.Design, 
    Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), 
    PersistChildren(true), ParseChildren(false)]
  public class ValidationGroup : Panel
  {
    public static readonly string ValidationGroupScriptKey = "__validationGroupScript";
    protected override void Render(HtmlTextWriter writer)
    {
      base.RenderChildren(writer);
    }
    protected override void OnPreRender(EventArgs e)
    {
      if(!Page.IsClientScriptBlockRegistered(ValidationGroupScriptKey)) 
      {
        string resourceName = typeof(ValidationGroup).Namespace + 
          ".Resources.ValidationGroup.js";
        using(StreamReader reader = new StreamReader(
          typeof(ValidationGroup).Assembly.GetManifestResourceStream(resourceName)))
        {
          Page.RegisterClientScriptBlock(ValidationGroupScriptKey,reader.ReadToEnd());
        }
      }
      RegisterValidators(this);
      base.OnPreRender (e);
    }
    public string GetValidationGroupScript() 
    {
      return string.Format("if (typeof({0}_Validators)!='undefined') " + 
         "{{ EnableValidationGroup({0}_Validators,{0}_InitialEnabled)}} else " + 
         "{{EnableValidationGroup();}};",ClientID);
    }
    public void RegisterControl(Button button) 
    {
      button.Attributes.Add("onclick",GetValidationGroupScript());
    }
    public void RegisterControl(LinkButton button) 
    {
      button.Attributes.Add("onclick",GetValidationGroupScript());
    }
    public bool IsValid 
    {
      get 
      {
        return CheckValidators(this);
      }
    }
    bool CheckValidators(Control c) 
    {
      bool isValid = true;
      foreach(Control childControl in c.Controls) 
      {
        if (childControl is BaseValidator && childControl.Visible) 
        {
          BaseValidator validator = (BaseValidator)childControl;
          if (validator.Enabled) 
          {
            isValid &= validator.IsValid;
          }
        } 
        else 
        {
          if (childControl is INamingContainer && childControl.Visible) 
          {
            isValid &= CheckValidators(childControl);
          }
        }
      }
      return isValid ;
    }
    void RegisterValidators(Control c) 
    {
      foreach(Control childControl in c.Controls) 
      {
        if (childControl is BaseValidator && childControl.Visible) 
        {
          Page.RegisterArrayDeclaration(ClientID + "_Validators",
            string.Format("document.all['{0}']",childControl.ClientID));
          Page.RegisterArrayDeclaration(ClientID + "_InitialEnabled",
            ((BaseValidator)childControl).Enabled.ToString().ToLower());
        } 
        else 
        {
          if (childControl is INamingContainer && childControl.Visible) 
          {
            RegisterValidators(childControl);
          }
        }
      }
    }
  }
}

Кроме того, как видно из кода, клиентский скрипт был вынесен в ресурсный файл

<script language="javascript">
  function EnableValidationGroup( activeValidators, initialValues ) {
    // Если на странице нет валидаторов то ничего не делаем
    if (typeof(Page_Validators)=='undefined') return;
    //Выключаем все валидаторы
    for( i = 0; i < Page_Validators.length; i++ )
      Page_Validators[i].enabled = false;
    if (typeof(activeValidators)=='undefined') return;
    //Включаем нужные нам валидаторы
    for( i = 0; i < activeValidators.length; i++ )
      activeValidators[i].enabled = initialValues[i];
  }
</script>

Ну и в конце, приведу пример использования нашего элемента управления в aspx страничке.

<%@ Page %>
<%@ Register tagPrefix="ej" Namespace="TestWebControls" Assembly="TestWebControls"%>
<%@ Import Namespace="System.Collections"%>
<head>
  <script runat="server" language="c#">
    void Page_Load(object sender, EventArgs e) {
      _group1.RegisterControl(_loginButton);
      _group2.RegisterControl(_postButton);
    }
    
    void OnLoginButtonClick(object sender, EventArgs e) {
      if (!_group1.IsValid) {
        return;
      }
      Response.Write("Login sucessfull");
    }
    void OnPostButtonClick(object sender, EventArgs e) {
      if (!_group2.IsValid) {
        return;
      }
      Response.Write("Post sucessfull");
    }    
  </script>
</head>
<body>
  <form runat="server">
    <table width="100%">
      <tr>
        <td width="200px" valign="top">
          <fieldset>
            <legend>Login</legend>
            <ej:ValidationGroup runat="server" id="_group1">
              <table width="200">
                <tr>
                  <td>Login:</td>
                  <td>
                    <asp:TextBox id="_loginText" runat="server" Width="150px" />
                    <asp:RequiredFieldValidator ErrorMessage="Login is required field"
                      runat="server" id="_loginRequired" Text="*"
                      Display="None" ControlToValidate="_loginText" />
                  </td>
                </tr>
                <tr>
                  <td>Password:</td>
                  <td><asp:TextBox id="_passwordText" TextMode="Password" 
                    runat="server" Width="150px" />
                    <asp:RequiredFieldValidator ErrorMessage="Password is required field"
                      runat="server" id="_passwordRequired"
                      Text="*" Display="None" ControlToValidate="_passwordText" />
                  </td>
                </tr>
                <tr>
                  <td></td>
                  <td><asp:Button runat="server" id="_loginButton" Text="Log In"
                    Width="100px" OnClick="OnLoginButtonClick" /></td>
                </tr>
              </table>
            </ej:ValidationGroup>
          </fieldset>
        </td>
        <td width="100%">
          <fieldset>
            <legend>Message Post</legend>
            <ej:ValidationGroup runat="server" id="_group2">
              <table>
                <tr>
                  <td>Title:</td>
                  <td><asp:TextBox id="_titleText" runat="server" Width="400px" />
                    <asp:RequiredFieldValidator ErrorMessage="Title is required field"
                      runat="server" id="_titleRequired" Text="*"
                      Display="None" ControlToValidate="_titleText" />
                  </td>
                </tr>
                <tr>
                  <td colspan="2">Message:</td>
                </tr>
                <tr>
                  <td colspan="2">
                    <asp:TextBox TextMode="MultiLine" Rows="20" id="_messageText" 
                      runat="server" Width="600px" />
                    <asp:RequiredFieldValidator runat="server" 
                      ErrorMessage="Message is required field" id="_messageRequired" Text="*"
                      Display="None" ControlToValidate="_messageText" />
                  </td>
                </tr>
                <tr>
                  <td align="right" colspan="2"><asp:Button id="_postButton" 
                    runat="server" Text="Post" Width="100px" 
                    OnClick="OnPostButtonClick" /></td>
                </tr>
              </table>
            </ej:ValidationGroup>
          </fieldset>
        </td>
      </tr>
    </table>
    <asp:ValidationSummary runat="server" ShowMessageBox="true" ShowSummary="false" />
  </form>
</body>

Вот и все.