נשאלה השאלה, איך אני מאגד את שלשת השדות של תאריך הלידה (יום, חודש ושנה) לערך אחד עבור ואלידציה (Validation)?
במצב הקיים, צריך להשתמש בשלש ואלידאטורים (Validators) מסוג RequiredFieldValidator ועוד 3 מסוג RangeValidator עבור טווח הערכים (למשל ימי החודש הם מ1 עד 31), וכל זה עדיין לא יעזור אם נרצה לבדוק טווח עבור כל התאריך בשלמות ולא עבור כל חלק שלו בנפרד. לפתרון לבעיה זו כתבתי WebControl שיורש מ – BaseCompareValidator בשם DateFieldsValidator.
הקוד הזה מציג שדה טקסט (TextBox) אחד עבור היום בחודש, ושני שדות בחירה (DropDownList) עבור החודש והשנה. הקוד ממלא את רשימת האפשרויות לבחירה של כל אחד משדות הDropDownList בערכים המתאימים לחודשים ושנים כשלכל אחד מהאפשרויות (הListItems ליתר בהירות) יש גם ערך מספרי שאיתו לבסוף מתבצעת הואלידאציה. בנוסף בראש כל רשימה הוספתי "ערך סרק" שמקבל ערך מספרי שלילי ובכך מכשיל את הוולידציה במקרה שהמשתמש לא יבחר ערך אמיתי של חודש ושנה.
ליצירת ה DateFieldsValidator פתחתי פרויקט חדש מסוג ASP.NET Server Control, בחרתי ב DanielKatz.Web.UIכRoot Namespace של הפרוייקט ויצרתי 3 קבצים: DateFieldsValidator.vb, DateFieldsValidator.js, AssemblyInfo.vb. הנה הקוד שלהם:
Imports System
Imports System.ComponentModel
Imports System.Globalization
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Security.Permissions
<AspNetHostingPermission(SecurityAction.LinkDemand, Level:=AspNetHostingPermissionLevel.Minimal)> _
<AspNetHostingPermission(SecurityAction.InheritanceDemand, Level:=AspNetHostingPermissionLevel.Minimal)> _
<ToolboxData("<{0}:DateFieldsValidator runat=server />")> _
Public Class DateFieldsValidator
Inherits BaseCompareValidator
#Region " Overriden Methods "
Protected Overloads Overrides Sub AddAttributesToRender(ByVal writer As System.Web.UI.HtmlTextWriter)
MyBase.Type = ValidationDataType.Date
MyBase.SetFocusOnError = False
MyBase.AddAttributesToRender(writer)
If Me.RenderUplevel Then
Dim maximumValue As String = Me.MaximumValue
Dim minimumValue As String = Me.MinimumValue
If minimumValue <> "" AndAlso maximumValue <> "" Then
If MyBase.CultureInvariantValues Then
maximumValue = ConvertCultureInvariantToCurrentCultureFormat(maximumValue)
minimumValue = ConvertCultureInvariantToCurrentCultureFormat(minimumValue)
End If
End If
Page.ClientScript.RegisterExpandoAttribute(ClientID, "evaluationfunction", "DateFieldsValidatorEvaluateIsValid")
Page.ClientScript.RegisterExpandoAttribute(ClientID, "dayControl", MyBase.GetControlRenderID(Me.DayToValidate))
Page.ClientScript.RegisterExpandoAttribute(ClientID, "monthControl", MyBase.GetControlRenderID(Me.MonthToValidate))
Page.ClientScript.RegisterExpandoAttribute(ClientID, "yearControl", MyBase.GetControlRenderID(Me.YearToValidate))
Page.ClientScript.RegisterExpandoAttribute(ClientID, "minimumValue", minimumValue)
Page.ClientScript.RegisterExpandoAttribute(ClientID, "maximumValue", maximumValue)
End If
End Sub
Protected Overloads Overrides Function ControlPropertiesValid() As Boolean
If Me.DayToValidate.Trim().Length = 0 Then
Throw New HttpException(String.Format("The DayToValidate property of {0} is empty.", Me.ID))
End If
If Me.MonthToValidate.Trim().Length = 0 Then
Throw New HttpException(String.Format("The MonthToValidate property of {0} is empty.", Me.ID))
End If
If Me.YearToValidate.Trim().Length = 0 Then
Throw New HttpException(String.Format("The YearToValidate property of {0} is empty.", Me.ID))
End If
MyBase.CheckControlValidationProperty(DayToValidate, "ControlsToValidate")
MyBase.CheckControlValidationProperty(MonthToValidate, "MonthToValidate")
MyBase.CheckControlValidationProperty(YearToValidate, "YearToValidate")
If Me.MinimumValue <> "" OrElse Me.MaximumValue <> "" Then
If Not BaseCompareValidator.CanConvert(MinimumValue, ValidationDataType.Date, MyBase.CultureInvariantValues) Then
Throw New HttpException("Invalid formatted value in MinimumValue")
End If
If Not BaseCompareValidator.CanConvert(MaximumValue, ValidationDataType.Date, MyBase.CultureInvariantValues) Then
Throw New HttpException("Invalid formatted value in MaximumValue")
End If
If BaseCompareValidator.Compare(MinimumValue, MyBase.CultureInvariantValues, MaximumValue, MyBase.CultureInvariantValues, ValidationCompareOperator.GreaterThan, ValidationDataType.Date) Then
Throw New HttpException("MinimumDate property cannot be greater then MaximumValue")
End If
End If
Return True
End Function
Protected Overloads Overrides Function EvaluateIsValid() As Boolean
Dim day As Integer = -1
Dim month As Integer = -1
Dim year As Integer = -1
Try
If Not Integer.TryParse(MyBase.GetControlValidationValue(Me.DayToValidate), day) Then
If Not (day >= 1 AndAlso day <= 31) Then
Return False
End If
End If
If Not Integer.TryParse(MyBase.GetControlValidationValue(Me.MonthToValidate), month) Then
If Not (month >= 1 AndAlso month <= 12) Then
Return False
End If
End If
If Not Integer.TryParse(MyBase.GetControlValidationValue(Me.YearToValidate), year) Then
If Not (year >= 1 AndAlso year <= 9999) Then
Return False
End If
End If
If (Me.MinimumValue <> "" AndAlso Me.MaximumValue <> "") Then
Dim val As Date = New Date(year, month, day)
Dim min As Date = ConvertCultureInvariantToCurrentCultureDate(Me.MinimumValue)
Dim max As Date = ConvertCultureInvariantToCurrentCultureDate(Me.MaximumValue)
Return (val >= min AndAlso val <= max)
Else
Return True
End If
Catch ex As Exception
Return False
End Try
End Function
Protected Overloads Overrides Sub OnPreRender(ByVal e As EventArgs)
MyBase.OnPreRender(e)
If MyBase.RenderUplevel Then
Me.Page.ClientScript.RegisterClientScriptResource(GetType(DateFieldsValidator), "DanielKatz.Web.UI.DateFieldsValidator.js")
End If
End Sub
#End Region
#Region " Helpers "
Private Function ConvertCultureInvariantToCurrentCultureFormat(ByVal valueInString As String) As String
Dim tmp As Object = Nothing
BaseCompareValidator.Convert(valueInString, Type, True, tmp)
If TypeOf tmp Is DateTime Then
Dim time As DateTime = CDate(tmp)
Return time.ToShortDateString
Else
Throw New HttpException("DateFieldsValidator supports only date value type")
End If
End Function
Private Function ConvertCultureInvariantToCurrentCultureDate(ByVal valueInString As String) As DateTime
Dim tmp As Object = Nothing
BaseCompareValidator.Convert(valueInString, Type, True, tmp)
If TypeOf tmp Is DateTime Then
Dim time As DateTime = CDate(tmp)
Return time
Else
Throw New HttpException("DateFieldsValidator supports only date value type")
End If
End Function
#End Region
#Region " Properties "
<Category("Behavior"), Themeable(False), DefaultValue(""), IDReferenceProperty(), TypeConverter(GetType(ValidatedControlConverter)), Description("ID of the day field")> _
Public Property DayToValidate() As String
Get
Return CType((If(ViewState("DayToValidate") Is Nothing, String.Empty, ViewState("DayToValidate"))), String)
End Get
Set(ByVal value As String)
ViewState("DayToValidate") = value
End Set
End Property
<Category("Behavior"), Themeable(False), DefaultValue(""), IDReferenceProperty(), TypeConverter(GetType(ValidatedControlConverter)), Description("ID of the month field")> _
Public Property MonthToValidate() As String
Get
Return CType((If(ViewState("MonthToValidate") Is Nothing, String.Empty, ViewState("MonthToValidate"))), String)
End Get
Set(ByVal value As String)
ViewState("MonthToValidate") = value
End Set
End Property
<Category("Behavior"), Themeable(False), DefaultValue(""), IDReferenceProperty(), TypeConverter(GetType(ValidatedControlConverter)), Description("ID of the year field")> _
Public Property YearToValidate() As String
Get
Return CType((If(ViewState("YearToValidate") Is Nothing, String.Empty, ViewState("YearToValidate"))), String)
End Get
Set(ByVal value As String)
ViewState("YearToValidate") = value
End Set
End Property
<Category("Behavior"), Themeable(False), DefaultValue(""), Description("Minimum date value, formatted as yyyy/MM/dd")> _
Public Property MinimumValue() As String
Get
Return CType((If(ViewState("MinimumValue") Is Nothing, String.Empty, ViewState("MinimumValue"))), String)
End Get
Set(ByVal value As String)
ViewState("MinimumValue") = value
End Set
End Property
<Category("Behavior"), Themeable(False), DefaultValue(""), Description("Maximum date value, formatted as yyyy/MM/dd")> _
Public Property MaximumValue() As String
Get
Return CType((If(ViewState("MaximumValue") Is Nothing, String.Empty, ViewState("MaximumValue"))), String)
End Get
Set(ByVal value As String)
ViewState("MaximumValue") = value
End Set
End Property
#End Region
#Region " Unsupported Properties "
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
Public Shadows Property ControlToValidate() As String
Get
Return String.Empty
End Get
Set(ByVal value As String)
Throw New NotSupportedException("The property isn't supported. Use DayToValidate, MonthToValidate, YearToValidate properties instead.")
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
Public Shadows Property SetFocusOnError() As Boolean
Get
Return False
End Get
Set(ByVal value As Boolean)
Throw New NotSupportedException("SetFocusOnError is not supported.")
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
Public Shadows Property Type() As ValidationDataType
Get
Return ValidationDataType.Date
End Get
Set(ByVal value As ValidationDataType)
Throw New NotSupportedException("Type is not supported.")
End Set
End Property
#End Region
End Class