|
//------------------------------------------------------------------------------
// <copyright file="BaseValidator.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Web.UI.WebControls {
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Web;
using System.Web.UI;
using System.Web.Util;
/// <devdoc>
/// <para> Serves as the abstract base
/// class for validator objects.</para>
/// </devdoc>
[
DefaultProperty("ErrorMessage"),
Designer("System.Web.UI.Design.WebControls.BaseValidatorDesigner, " + AssemblyRef.SystemDesign),
]
public abstract class BaseValidator : Label, IValidator {
private const string UnobtrusivePrefix = "data-val-";
private const string jqueryScriptKey = "jquery";
// constants for Validation script library
private const string ValidatorFileName = "WebUIValidation.js";
private const string ValidatorIncludeScriptKey = "ValidatorIncludeScript";
private const string ValidatorStartupScript = @"
var Page_ValidationActive = false;
if (typeof(ValidatorOnLoad) == ""function"") {
ValidatorOnLoad();
}
function ValidatorOnSubmit() {
if (Page_ValidationActive) {
return ValidatorCommonOnSubmit();
}
else {
return true;
}
}
";
private bool preRenderCalled;
private bool isValid;
private bool propertiesChecked;
private bool propertiesValid;
private bool renderUplevel;
private bool wasForeColorSet = false;
/// <devdoc>
/// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.BaseValidator'/> class.</para>
/// </devdoc>
protected BaseValidator() {
isValid = true;
propertiesChecked = false;
propertiesValid = true;
renderUplevel = false;
}
protected bool IsUnobtrusive {
get {
return (Page != null && Page.UnobtrusiveValidationMode != UnobtrusiveValidationMode.None);
}
}
[
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public override string AssociatedControlID {
get {
return base.AssociatedControlID;
}
set {
throw new NotSupportedException(
SR.GetString(SR.Property_Not_Supported,
"AssociatedControlID",
this.GetType().ToString()));
}
}
/// <devdoc>
/// <para>Gets or sets
/// the text color of validation messages.</para>
/// </devdoc>
[
DefaultValue(typeof(Color), "Red")
]
public override Color ForeColor {
get {
return base.ForeColor;
}
set {
wasForeColorSet = true;
base.ForeColor = value;
}
}
/// <devdoc>
/// <para>Gets or sets the control to validate.</para>
/// </devdoc>
[
WebCategory("Behavior"),
Themeable(false),
DefaultValue(""),
IDReferenceProperty(),
WebSysDescription(SR.BaseValidator_ControlToValidate),
TypeConverter(typeof(ValidatedControlConverter))
]
public string ControlToValidate {
get {
object o = ViewState["ControlToValidate"];
return((o == null) ? String.Empty : (string)o);
}
set {
ViewState["ControlToValidate"] = value;
}
}
/// <devdoc>
/// <para>Gets or sets the text for the error message.</para>
/// </devdoc>
[
Localizable(true),
WebCategory("Appearance"),
DefaultValue(""),
WebSysDescription(SR.BaseValidator_ErrorMessage)
]
public string ErrorMessage {
get {
object o = ViewState["ErrorMessage"];
return((o == null) ? String.Empty : (string)o);
}
set {
ViewState["ErrorMessage"] = value;
}
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
[
WebCategory("Behavior"),
Themeable(false),
DefaultValue(true),
WebSysDescription(SR.BaseValidator_EnableClientScript)
]
public bool EnableClientScript {
get {
object o = ViewState["EnableClientScript"];
return((o == null) ? true : (bool)o);
}
set {
ViewState["EnableClientScript"] = value;
}
}
/// <devdoc>
/// <para>Gets or sets a value that indicates whether the validation for the control is
/// enabled.</para>
/// </devdoc>
public override bool Enabled {
get {
return base.Enabled;
}
set {
base.Enabled= value;
// When disabling a validator, it would almost always be intended for that validator
// to not make the page invalid for that round-trip.
if (!value) {
isValid = true;
}
}
}
// VSWhidbey 244999
internal override bool IsReloadable {
get {
return true;
}
}
/// <devdoc>
/// <para>Gets
/// or sets a flag to indicate if the referenced control passed
/// validation.</para>
/// </devdoc>
[
Browsable(false),
WebCategory("Behavior"),
Themeable(false),
DefaultValue(true),
WebSysDescription(SR.BaseValidator_IsValid),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)
]
public bool IsValid {
get {
return isValid;
}
set {
isValid = value;
}
}
/// <devdoc>
/// <para>Gets a value that indicates whether the property of the control is valid. This property is read-only.</para>
/// </devdoc>
protected bool PropertiesValid {
get {
if (!propertiesChecked) {
propertiesValid = ControlPropertiesValid();
propertiesChecked = true;
}
return propertiesValid;
}
}
/// <devdoc>
/// <para>Gets a value that indicates whether the client's browser supports uplevel rendering. This
/// property is read-only.</para>
/// </devdoc>
protected bool RenderUplevel {
get {
return renderUplevel;
}
}
// This property should be Themeable. (DevDiv Bugs 156845)
/// <devdoc>
/// <para>Gets or sets the display behavior of the
/// validator control.</para>
/// </devdoc>
[
WebCategory("Appearance"),
Themeable(true),
DefaultValue(ValidatorDisplay.Static),
WebSysDescription(SR.BaseValidator_Display)
]
public ValidatorDisplay Display {
get {
object o = ViewState["Display"];
return((o == null) ? ValidatorDisplay.Static : (ValidatorDisplay)o);
}
set {
if (value < ValidatorDisplay.None || value > ValidatorDisplay.Dynamic) {
throw new ArgumentOutOfRangeException("value");
}
ViewState["Display"] = value;
}
}
/// <devdoc>
/// Whether the validator sets focus on the control when invalid
/// </devdoc>
[
WebCategory("Behavior"),
Themeable(false),
DefaultValue(false),
WebSysDescription(SR.BaseValidator_SetFocusOnError)
]
public bool SetFocusOnError {
get {
object o = ViewState["SetFocusOnError"];
return((o == null) ? false : (bool)o);
}
set {
ViewState["SetFocusOnError"] = value;
}
}
/// <devdoc>
/// <para>Text to display for the validator when the validated control is invalid.</para>
/// </devdoc>
[
WebCategory("Appearance"),
DefaultValue(""),
WebSysDescription(SR.BaseValidator_Text),
PersistenceMode(PersistenceMode.InnerDefaultProperty)
]
public override string Text {
// VSWhidbey 83105: Override the property only to override the description
get {
return base.Text;
}
set {
base.Text = value;
}
}
[
WebCategory("Behavior"),
Themeable(false),
DefaultValue(""),
WebSysDescription(SR.BaseValidator_ValidationGroup)
]
public virtual string ValidationGroup {
get {
object o = ViewState["ValidationGroup"];
return((o == null) ? string.Empty : (string)o);
}
set {
ViewState["ValidationGroup"] = value;
}
}
/// <internalonly/>
/// <devdoc>
/// <para>Adds the attributes of this control to the output stream for rendering on the
/// client.</para>
/// </devdoc>
protected override void AddAttributesToRender(HtmlTextWriter writer) {
// Validators do not render the "disabled" attribute, instead they are invisible when disabled.
bool disabled = !Enabled;
if (disabled) {
Enabled = true;
}
try {
if (RenderUplevel) {
// We always want validators to have an id on the client
EnsureID();
string id = ClientID;
// DevDiv Schedule 33075: Expando attributes are added through client-side JavaScript
// DevDiv 33149: A backward compat. switch for Everett rendering
HtmlTextWriter expandoAttributeWriter = (EnableLegacyRendering || IsUnobtrusive) ? writer : null;
if (IsUnobtrusive) {
Attributes["data-val"] = "true";
}
if (ControlToValidate.Length > 0) {
AddExpandoAttribute(expandoAttributeWriter, id, "controltovalidate", GetControlRenderID(ControlToValidate));
}
if (SetFocusOnError) {
AddExpandoAttribute(expandoAttributeWriter, id, "focusOnError", "t", false);
}
if (ErrorMessage.Length > 0) {
AddExpandoAttribute(expandoAttributeWriter, id, "errormessage", ErrorMessage);
}
ValidatorDisplay display = Display;
if (display != ValidatorDisplay.Static) {
AddExpandoAttribute(expandoAttributeWriter, id, "display", PropertyConverter.EnumToString(typeof(ValidatorDisplay), display), false);
}
if (!IsValid) {
AddExpandoAttribute(expandoAttributeWriter, id, "isvalid", "False", false);
}
if (disabled) {
AddExpandoAttribute(expandoAttributeWriter, id, "enabled", "False", false);
}
if (ValidationGroup.Length > 0) {
AddExpandoAttribute(expandoAttributeWriter, id, "validationGroup", ValidationGroup);
}
}
base.AddAttributesToRender(writer);
}
finally {
// If exception happens above, we can still reset the property if needed
if (disabled) {
Enabled = false;
}
}
}
internal void AddExpandoAttribute(HtmlTextWriter writer, string controlId, string attributeName, string attributeValue) {
AddExpandoAttribute(writer, controlId, attributeName, attributeValue, true);
}
internal void AddExpandoAttribute(HtmlTextWriter writer, string controlId, string attributeName, string attributeValue, bool encode) {
AddExpandoAttribute(this, writer, controlId, attributeName, attributeValue, encode);
}
internal static void AddExpandoAttribute(Control control, HtmlTextWriter writer, string controlId, string attributeName, string attributeValue, bool encode) {
Debug.Assert(control != null);
Page page = control.Page;
Debug.Assert(page != null);
// if writer is not null, assuming the expando attribute is written out explicitly
if (writer != null) {
if (page.UnobtrusiveValidationMode != UnobtrusiveValidationMode.None) {
attributeName = UnobtrusivePrefix + attributeName;
}
writer.AddAttribute(attributeName, attributeValue, encode);
}
else {
Debug.Assert(page.UnobtrusiveValidationMode == UnobtrusiveValidationMode.None, "The writer must have been passed in the Unobtrusive mode");
// Cannot use the overload of RegisterExpandoAttribute that takes a Control, since that method only works with AJAX 3.5,
// and we need to support Validators in AJAX 1.0 (Windows OS Bugs 2015831).
if (!page.IsPartialRenderingSupported) {
// Fall back to ASP.NET 2.0 behavior
page.ClientScript.RegisterExpandoAttribute(controlId, attributeName, attributeValue, encode);
}
else {
// Atlas Partial Rendering support
// ScriptManager exists, so call its instance' method for script registration
ValidatorCompatibilityHelper.RegisterExpandoAttribute(control, controlId, attributeName, attributeValue, encode);
}
}
}
/// <devdoc>
/// <para> Determines if the referenced control
/// has a validation property.</para>
/// </devdoc>
protected void CheckControlValidationProperty(string name, string propertyName) {
// get the control using the relative name
Control c = NamingContainer.FindControl(name);
if (c == null) {
throw new HttpException(
SR.GetString(SR.Validator_control_not_found, name, propertyName, ID));
}
// get its validation property
PropertyDescriptor prop = GetValidationProperty(c);
if (prop == null) {
throw new HttpException(
SR.GetString(SR.Validator_bad_control_type, name, propertyName, ID));
}
}
/// <devdoc>
/// <para>Determines if the properties are valid so that validation
/// is meaningful.</para>
/// </devdoc>
protected virtual bool ControlPropertiesValid() {
// Check for blank control to validate
string controlToValidate = ControlToValidate;
if (controlToValidate.Length == 0) {
throw new HttpException(
SR.GetString(SR.Validator_control_blank, ID));
}
// Check that the property points to a valid control. Will throw and exception if not found
CheckControlValidationProperty(controlToValidate, "ControlToValidate");
return true;
}
/// <devdoc>
/// <para>[To be supplied.]</para>
/// </devdoc>
protected virtual bool DetermineRenderUplevel() {
// must be on a page
Page page = Page;
if (page == null || page.RequestInternal == null) {
return false;
}
// Check the browser capabilities
return (EnableClientScript
&& page.Request.Browser.W3CDomVersion.Major >= 1
&& page.Request.Browser.EcmaScriptVersion.CompareTo(new Version(1, 2)) >= 0);
}
/// <devdoc>
/// <para> TDB. Not
/// coded yet.</para>
/// </devdoc>
protected abstract bool EvaluateIsValid();
/// <devdoc>
/// Gets the control indicated by the relative name and
/// returns an ID that can be used on the client.
/// </devdoc>
protected string GetControlRenderID(string name) {
// get the control using the relative name
Control c = FindControl(name);
if (c == null) {
Debug.Fail("We should have already checked for the presence of this");
return String.Empty;
}
return c.ClientID;
}
/// <devdoc>
/// <para> Gets the validation value of the control
/// named relative to the validator.</para>
/// </devdoc>
protected string GetControlValidationValue(string name) {
// get the control using the relative name
Control c = NamingContainer.FindControl(name);
if (c == null) {
return null;
}
// get its validation property
PropertyDescriptor prop = GetValidationProperty(c);
if (prop == null) {
return null;
}
// get its value as a string
object value = prop.GetValue(c);
if (value is ListItem) {
return((ListItem) value).Value;
}
else if (value != null) {
return value.ToString();
}
else {
return string.Empty;
}
}
/// <devdoc>
/// <para>Helper function to get the validation
/// property of a control if it exists.</para>
/// </devdoc>
public static PropertyDescriptor GetValidationProperty(object component) {
ValidationPropertyAttribute valProp = (ValidationPropertyAttribute)TypeDescriptor.GetAttributes(component)[typeof(ValidationPropertyAttribute)];
if (valProp != null && valProp.Name != null) {
return TypeDescriptor.GetProperties(component, null)[valProp.Name];
}
return null;
}
/// <internalonly/>
/// <devdoc>
/// <para> Registers the validator on the page.</para>
/// </devdoc>
protected internal override void OnInit(EventArgs e) {
base.OnInit(e);
if (!wasForeColorSet && (RenderingCompatibility < VersionUtil.Framework40 )) {
// If the ForeColor wasn't already set, try to set our dynamic default value
ForeColor = Color.Red;
}
Page.Validators.Add(this);
}
/// <internalonly/>
/// <devdoc>
/// <para> Un-registers the validator on the page.</para>
/// </devdoc>
protected internal override void OnUnload(EventArgs e) {
if (Page != null) {
Page.Validators.Remove(this);
}
base.OnUnload(e);
}
/// <internalonly/>
/// <devdoc>
/// <para>Checks the client brower and configures the
/// validator for compatibility prior to rendering. </para>
/// </devdoc>
protected internal override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
preRenderCalled = true;
// force a requery of properties for render
propertiesChecked = false;
// VSWhidbey 83130, we should check properties during PreRender so
// the checking applies to all deviecs.
if (!PropertiesValid) {
// In practice the call to the property PropertiesValid would
// throw if bad things happen.
Debug.Assert(false, "Exception should have been thrown if properties are invalid");
}
// work out uplevelness now
renderUplevel = DetermineRenderUplevel();
if (IsUnobtrusive && EnableClientScript) {
RegisterUnobtrusiveScript();
}
if (renderUplevel) {
RegisterValidatorCommonScript();
}
}
/// <devdoc>
/// <para>
/// Registers code on the page for client-side validation.
/// </para>
/// </devdoc>
protected void RegisterValidatorCommonScript() {
const string onSubmitScriptKey = "ValidatorOnSubmit";
const string onSubmitScript = "if (typeof(ValidatorOnSubmit) == \"function\" && ValidatorOnSubmit() == false) return false;";
// Cannot use the overloads of Register* that take a Control, since these methods only work with AJAX 3.5,
// and we need to support Validators in AJAX 1.0 (Windows OS Bugs 2015831).
if (!Page.IsPartialRenderingSupported) {
if (Page.ClientScript.IsClientScriptBlockRegistered(typeof(BaseValidator), ValidatorIncludeScriptKey)) {
return;
}
Page.ClientScript.RegisterClientScriptResource(typeof(BaseValidator), ValidatorFileName);
Page.ClientScript.RegisterOnSubmitStatement(typeof(BaseValidator), onSubmitScriptKey, onSubmitScript);
if (!IsUnobtrusive) {
Page.ClientScript.RegisterStartupScript(typeof(BaseValidator), ValidatorIncludeScriptKey, ValidatorStartupScript, addScriptTags: true);
}
}
else {
// Register the original validation scripts but through the new ScriptManager APIs
ValidatorCompatibilityHelper.RegisterClientScriptResource(this, typeof(BaseValidator), ValidatorFileName);
ValidatorCompatibilityHelper.RegisterOnSubmitStatement(this, typeof(BaseValidator), onSubmitScriptKey, onSubmitScript);
if (!IsUnobtrusive) {
ValidatorCompatibilityHelper.RegisterStartupScript(this, typeof(BaseValidator), ValidatorIncludeScriptKey, ValidatorStartupScript, addScriptTags: true);
}
}
}
internal void RegisterUnobtrusiveScript() {
ClientScriptManager.EnsureJqueryRegistered();
ValidatorCompatibilityHelper.RegisterClientScriptResource(this, jqueryScriptKey);
}
/// <devdoc>
/// <para>Registers array declarations using the default array, <see langword='Page_Validators'/> .</para>
/// </devdoc>
protected virtual void RegisterValidatorDeclaration() {
const string arrayName = "Page_Validators";
string element = "document.getElementById(\"" + ClientID + "\")";
// Cannot use the overloads of Register* that take a Control, since these methods only work with AJAX 3.5,
// and we need to support Validators in AJAX 1.0 (Windows OS Bugs 2015831).
if (!Page.IsPartialRenderingSupported) {
Page.ClientScript.RegisterArrayDeclaration(arrayName, element);
}
else {
ValidatorCompatibilityHelper.RegisterArrayDeclaration(this, arrayName, element);
// Register a dispose script to make sure we clean up the page if we get destroyed
// during an async postback.
// We should technically use the ScriptManager.RegisterDispose() method here, but the original implementation
// of Validators in AJAX 1.0 manually attached a dispose expando. We added this code back in the product
// late in the Orcas cycle, and we didn't want to take the risk of using RegisterDispose() instead.
// (Windows OS Bugs 2015831)
ValidatorCompatibilityHelper.RegisterStartupScript(this, typeof(BaseValidator), ClientID + "_DisposeScript",
String.Format(
CultureInfo.InvariantCulture,
@"
document.getElementById('{0}').dispose = function() {{
Array.remove({1}, document.getElementById('{0}'));
}}
",
ClientID, arrayName), true);
}
}
/// <internalonly/>
/// <devdoc>
/// <para>Displays the control on the client.</para>
/// </devdoc>
protected internal override void Render(HtmlTextWriter writer) {
bool shouldBeVisible;
// VSWhidbey 347677, 398978: Backward Compat.: Skip property checking if the
// validator doesn't have PreRender called and it is not in page control tree.
if (DesignMode || (!preRenderCalled && Page == null)) {
// This is for design time. In this case we don't want any expandos
// created, don't want property checks and always want to be visible.
propertiesChecked = true;
propertiesValid = true;
renderUplevel = false;
shouldBeVisible = true;
}
else {
shouldBeVisible = Enabled && !IsValid;
}
// No point rendering if we have errors
if (!PropertiesValid) {
return;
}
// Make sure we are in a form tag with runat=server.
if (Page != null) {
Page.VerifyRenderingInServerForm(this);
}
// work out what we are displaying
ValidatorDisplay display = Display;
bool displayContents;
bool displayTags;
if (RenderUplevel) {
displayTags = true;
displayContents = (display != ValidatorDisplay.None);
}
else {
displayContents = (display != ValidatorDisplay.None && shouldBeVisible);
displayTags = displayContents;
}
if (displayTags && RenderUplevel) {
if (!IsUnobtrusive) {
// Put ourselves in the array
RegisterValidatorDeclaration();
}
// Set extra uplevel styles
if (display == ValidatorDisplay.None
|| (!shouldBeVisible && display == ValidatorDisplay.Dynamic)) {
Style["display"] = "none";
}
else if (!shouldBeVisible) {
Debug.Assert(display == ValidatorDisplay.Static, "Unknown Display Type");
Style["visibility"] = "hidden";
}
}
// Display it
if (displayTags) {
RenderBeginTag(writer);
}
if (displayContents) {
if (Text.Trim().Length > 0) {
RenderContents(writer);
}
else if (HasRenderingData()) {
base.RenderContents(writer);
}
else {
writer.Write(ErrorMessage);
}
}
else if (!RenderUplevel && display == ValidatorDisplay.Static) {
// For downlevel in static mode, render a space so that table cells do not render as empty
writer.Write(" ");
}
if (displayTags) {
RenderEndTag(writer);
}
}
internal bool ShouldSerializeForeColor() {
Color defaultForeColor = (RenderingCompatibility < VersionUtil.Framework40) ? Color.Red : Color.Empty;
return defaultForeColor != ForeColor;
}
/// <devdoc>
/// <para>Evaluates validity and updates the <see cref='System.Web.UI.WebControls.BaseValidator.IsValid'/> property.</para>
/// </devdoc>
public void Validate() {
IsValid = true;
if (!Visible || !Enabled) {
return;
}
propertiesChecked = false;
if (!PropertiesValid) {
return;
}
IsValid = EvaluateIsValid();
Debug.Trace("BaseValidator.Validate", "id:" + ID + ", evaluateIsValid = " + IsValid.ToString());
if (!IsValid) {
Page page = Page;
if (page != null && SetFocusOnError) {
// Dev10 584609 Need to render ClientID not control id for auto focus to work
string validateId = ControlToValidate;
Control c = NamingContainer.FindControl(validateId);
if (c != null) {
validateId = c.ClientID;
}
Page.SetValidatorInvalidControlFocus(validateId);
}
}
}
}
}
|