File: UI\WebControls\CheckBox.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="CheckBox.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Web.UI.WebControls {
    using System;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Globalization;
    using System.Web;
    using System.Web.UI;
    using System.Web.Util;
    using AttributeCollection = System.Web.UI.AttributeCollection;
 
 
    /// <devdoc>
    ///    <para>Represents a Windows checkbox control.</para>
    /// </devdoc>
    [
    ControlValueProperty("Checked"),
    DataBindingHandler("System.Web.UI.Design.TextDataBindingHandler, " + AssemblyRef.SystemDesign),
    DefaultEvent("CheckedChanged"),
    Designer("System.Web.UI.Design.WebControls.CheckBoxDesigner, " + AssemblyRef.SystemDesign),
    DefaultProperty("Text"),
    SupportsEventValidation,
    ]
    public class CheckBox : WebControl, IPostBackDataHandler, ICheckBoxControl {
        internal AttributeCollection _inputAttributes;
        private StateBag _inputAttributesState;
        private AttributeCollection _labelAttributes;
        private StateBag _labelAttributesState;
        private string _valueAttribute = null;
 
        private static readonly object EventCheckedChanged = new object();
 
 
        /// <devdoc>
        /// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.CheckBox'/> class.</para>
        /// </devdoc>
        public CheckBox() : base(HtmlTextWriterTag.Input) {
        }
 
 
        /// <devdoc>
        /// <para>Gets or sets a value indicating that the <see cref='System.Web.UI.WebControls.CheckBox'/> state is automatically posted back to
        ///    the
        ///    server.</para>
        /// </devdoc>
        [
        DefaultValue(false),
        WebCategory("Behavior"),
        WebSysDescription(SR.CheckBox_AutoPostBack),
        Themeable(false),
        ]
        public virtual bool AutoPostBack {
            get {
                object b = ViewState["AutoPostBack"];
                return((b == null) ? false : (bool)b);
            }
            set {
                ViewState["AutoPostBack"] = value;
            }
        }
 
 
        [
        DefaultValue(false),
        WebCategory("Behavior"),
        WebSysDescription(SR.AutoPostBackControl_CausesValidation),
        Themeable(false),
        ]
        public virtual bool CausesValidation {
            get {
                object b = ViewState["CausesValidation"];
                return((b == null) ? false : (bool)b);
            }
            set {
                ViewState["CausesValidation"] = value;
            }
        }
 
 
        /// <devdoc>
        ///    <para>Gets or sets a value indicating the checked state of the
        ///    <see cref='System.Web.UI.WebControls.CheckBox'/>.</para>
        /// </devdoc>
        [
        Bindable(true, BindingDirection.TwoWay),
        DefaultValue(false),
        Themeable(false),
        WebSysDescription(SR.CheckBox_Checked),
        ]
        public virtual bool Checked {
            get {
                object b = ViewState["Checked"];
                return((b == null) ? false : (bool)b);
            }
            set {
                ViewState["Checked"] = value;
            }
        }
 
 
        /// <devdoc>
        ///    <para>Attribute collection for the rendered input element.</para>
        /// </devdoc>
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        WebSysDescription(SR.CheckBox_InputAttributes)
        ]
        public AttributeCollection InputAttributes {
            get {
                if (_inputAttributes == null) {
 
                    if (_inputAttributesState == null) {
                        _inputAttributesState = new StateBag(true);
                        if (IsTrackingViewState)
                            _inputAttributesState.TrackViewState();
                    }
 
                    _inputAttributes = new AttributeCollection(_inputAttributesState);
                }
                return _inputAttributes;
            }
        }
 
 
        /// <devdoc>
        ///    <para>Attribute collection for the rendered span element.</para>
        /// </devdoc>
        [
        Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
        WebSysDescription(SR.CheckBox_LabelAttributes)
        ]
        public AttributeCollection LabelAttributes {
            get {
                if (_labelAttributes == null) {
 
                    if (_labelAttributesState == null) {
                        _labelAttributesState = new StateBag(true);
                        if (IsTrackingViewState)
                            _labelAttributesState.TrackViewState();
                    }
 
                    _labelAttributes = new AttributeCollection(_labelAttributesState);
                }
                return _labelAttributes;
            }
        }
 
 
        internal override bool RequiresLegacyRendering {
            get {
                return true;
            }
        }
 
 
        /// <devdoc>
        ///   Controls whether the Checked property is saved in ViewState.
        ///   This is used for optimizing the size of the view state.
        /// </devdoc>
        private bool SaveCheckedViewState(bool autoPostBack) {
            // Must be saved when
            // 1. There is a registered event handler for SelectedIndexChanged
            // 2. Control is not enabled or visible, because the browser's post data will not include this control
            // 3. The instance is a derived instance, which might be overriding the OnSelectedIndexChanged method
            //    This is a bit hacky, since we have to cover all the four derived classes we have...
            // 4. AutoPostBack is true and Adapter doesn't support JavaScript
            //    For CheckBoxes to behave the same on mobile devices
            //    that simulate AutoPostBack by rendering a command button, we need to save
            //    state
            //    
 
            if ((Events[EventCheckedChanged] != null) ||
                (IsEnabled == false) ||
                (Visible == false) ||
                (autoPostBack == true && ((Page != null) && !Page.ClientSupportsJavaScript))) {
 
                return true;
            }
 
            Type t = this.GetType();
            if ((t == typeof(CheckBox)) || (t == typeof(RadioButton))) {
                return false;
            }
 
            return true;
        }
 
 
        /// <devdoc>
        /// <para>Gets or sets the text label associated with the <see cref='System.Web.UI.WebControls.CheckBox'/>.</para>
        /// </devdoc>
        [
        Bindable(true),
        Localizable(true),
        WebCategory("Appearance"),
        DefaultValue(""),
        WebSysDescription(SR.CheckBox_Text)
        ]
        public virtual string Text {
            get {
                string s = (string)ViewState["Text"];
                return((s == null) ? String.Empty : s);
            }
            set {
                ViewState["Text"] = value;
            }
        }
 
 
        /// <devdoc>
        /// <para>Gets or sets the alignment of the <see langword='Text'/> associated with the <see cref='System.Web.UI.WebControls.CheckBox'/>.</para>
        /// </devdoc>
        [
        WebCategory("Appearance"),
        DefaultValue(TextAlign.Right),
        WebSysDescription(SR.WebControl_TextAlign)
        ]
        public virtual TextAlign TextAlign {
            get {
                object align = ViewState["TextAlign"];
                return((align == null) ? TextAlign.Right : (TextAlign)align);
            }
            set {
                if (value < TextAlign.Left || value > TextAlign.Right) {
                    throw new ArgumentOutOfRangeException("value");
                }
                ViewState["TextAlign"] = value;
            }
        }
 
 
        [
        DefaultValue(""),
        Themeable(false),
        WebCategory("Behavior"),
        WebSysDescription(SR.PostBackControl_ValidationGroup),
        ]
        public virtual string ValidationGroup {
            get {
                string s = (string)ViewState["ValidationGroup"];
                return((s == null) ? String.Empty : s);
            }
            set {
                ViewState["ValidationGroup"] = value;
            }
        }
 
 
        /// <devdoc>
        /// <para>Occurs when the <see cref='System.Web.UI.WebControls.CheckBox'/> is clicked.</para>
        /// </devdoc>
        [
        WebCategory("Action"),
        WebSysDescription(SR.Control_OnServerCheckChanged)
        ]
        public event EventHandler CheckedChanged {
            add {
                Events.AddHandler(EventCheckedChanged, value);
            }
            remove {
                Events.RemoveHandler(EventCheckedChanged, value);
            }
        }
 
 
        /// <devdoc>
        ///     Adds attributes to be rendered.
        /// </devdoc>
        protected override void AddAttributesToRender(HtmlTextWriter writer) {
            // VSWhidbey 460446
            AddDisplayInlineBlockIfNeeded(writer);
 
            // Everett's behavior is that we didn't call the base method.
        }
 
 
        /// <devdoc>
        ///     Loads the view state for the control.
        /// </devdoc>
        protected override void LoadViewState(object savedState) {
            if (savedState != null) {
                Triplet stateTriplet = (Triplet)savedState;
                base.LoadViewState(stateTriplet.First);
                if (stateTriplet.Second != null) {
                    if (_inputAttributesState == null) {
                        _inputAttributesState = new StateBag();
                        _inputAttributesState.TrackViewState();
                    }
                    _inputAttributesState.LoadViewState(stateTriplet.Second);
                }
                if (stateTriplet.Third != null) {
                    if (_labelAttributesState == null) {
                        _labelAttributesState = new StateBag();
                        _labelAttributesState.TrackViewState();
                    }
                    _labelAttributesState.LoadViewState(BinaryCompatibility.Current.TargetsAtLeastFramework48 ? stateTriplet.Third : stateTriplet.Second);
                }
            }
        }
 
 
        /// <devdoc>
        ///    <para> Raises the
        ///    <see langword='CheckedChanged'/> event of the <see cref='System.Web.UI.WebControls.CheckBox'/>
        ///    controls.</para>
        /// </devdoc>
        protected virtual void OnCheckedChanged(EventArgs e) {
            EventHandler handler = (EventHandler)Events[EventCheckedChanged];
            if (handler != null) {
                handler(this, e);
            }
        }
 
 
        /// <devdoc>
        ///    <para>Registers client script for generating postback prior to
        ///       rendering on the client if <see cref='System.Web.UI.WebControls.CheckBox.AutoPostBack'/> is
        ///    <see langword='true'/>.</para>
        /// </devdoc>
        protected internal override void OnPreRender(EventArgs e) {
            base.OnPreRender(e);
            bool autoPostBack = AutoPostBack;
 
            if (Page != null && IsEnabled) {
                // we always need to get post back data
                Page.RegisterRequiresPostBack(this);
                if (autoPostBack) {
                    Page.RegisterPostBackScript();
                    Page.RegisterFocusScript();
 
                    // VSWhidbey 489577: It would handle both CheckBox and RadioButton cases
                    if (CausesValidation && Page.GetValidators(ValidationGroup).Count > 0) {
                        Page.RegisterWebFormsScript();
                    }
                }
            }
 
            if (!SaveCheckedViewState(autoPostBack)) {
                ViewState.SetItemDirty("Checked", false);
 
                if ((Page != null) && IsEnabled) {
                    // Store a client-side array of enabled control, so we can re-enable them on
                    // postback (in case they are disabled client-side)
                    Page.RegisterEnabledControl(this);
                }
            }
        }
 
 
        /// <devdoc>
        ///     Saves the view state for the control.
        /// </devdoc>
        protected override object SaveViewState() {
            object baseState = base.SaveViewState();
            object inputState = null;
            object labelState = null;
            object myState = null;
 
            if (_inputAttributesState != null) {
                inputState = _inputAttributesState.SaveViewState();
            }
            if (_labelAttributesState != null) {
                labelState = _labelAttributesState.SaveViewState();
            }
            if (baseState != null || inputState != null || labelState != null) {
                myState = new Triplet(baseState, inputState, labelState);
            }
            return myState;
        }
 
 
        /// <devdoc>
        ///     Starts view state tracking.
        /// </devdoc>
        protected override void TrackViewState() {
            base.TrackViewState();
            if (_inputAttributesState != null) {
                _inputAttributesState.TrackViewState();
            }
            if (_labelAttributesState != null) {
                _labelAttributesState.TrackViewState();
            }
        }
 
 
        /// <internalonly/>
        /// <devdoc>
        /// <para>Displays the <see cref='System.Web.UI.WebControls.CheckBox'/> on the client.</para>
        /// </devdoc>
        protected internal override void Render(HtmlTextWriter writer) {
            AddAttributesToRender(writer);
 
            // Make sure we are in a form tag with runat=server.
            if (Page != null) {
                Page.VerifyRenderingInServerForm(this);
            }
 
            bool renderWrapper = false;
 
            // On wrapper, render ---- attribute and class according to RenderingCompatibility
            if (!IsEnabled) {
                if (RenderingCompatibility < VersionUtil.Framework40) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");
                    renderWrapper = true;
                }
                else if (!Enabled && !String.IsNullOrEmpty(DisabledCssClass)) {
                    if (String.IsNullOrEmpty(CssClass)) {
                        ControlStyle.CssClass = DisabledCssClass;
                    }
                    else {
                        ControlStyle.CssClass = DisabledCssClass + " " + CssClass;
                    }
                    renderWrapper = true;
                }
            }
 
            // And Style
            if (ControlStyleCreated) {
                Style controlStyle = ControlStyle;
                if (!controlStyle.IsEmpty) {
                    controlStyle.AddAttributesToRender(writer, this);
                    renderWrapper = true;
                }
            }
            // And ToolTip
            string toolTip = ToolTip;
            if (toolTip.Length > 0) {
                writer.AddAttribute(HtmlTextWriterAttribute.Title, toolTip);
                renderWrapper = true;
            }
 
            string onClick = null;
            // And other attributes
            if (HasAttributes) {
                AttributeCollection attribs = Attributes;
 
                // remove value from the attribute collection so it's not on the wrapper
                string val = attribs["value"];
                if (val != null)
                    attribs.Remove("value");
 
                // remove and save onclick from the attribute collection so we can move it to the input tag
                onClick = attribs["onclick"];
                if (onClick != null) {
                    onClick = Util.EnsureEndWithSemiColon(onClick);
                    attribs.Remove("onclick");
                }
 
                if (attribs.Count != 0)
                {
                    attribs.AddAttributes(writer);
                    renderWrapper = true;
                }
 
                if (val != null)
                    attribs["value"] = val;
            }
 
            // render begin tag of wrapper SPAN
            if (renderWrapper) {
                writer.RenderBeginTag(HtmlTextWriterTag.Span);
            }
 
            string text = Text;
            string clientID = ClientID;
            if (text.Length != 0) {
                if (TextAlign == TextAlign.Left) {
                    // render label to left of checkbox
                    RenderLabel(writer, text, clientID);
                    RenderInputTag(writer, clientID, onClick);
                }
                else {
                    // render label to right of checkbox
                    RenderInputTag(writer, clientID, onClick);
                    RenderLabel(writer, text, clientID);
                }
            }
            else
                RenderInputTag(writer, clientID, onClick);
 
            // render end tag of wrapper SPAN
            if (renderWrapper) {
                writer.RenderEndTag();
            }
        }
 
        private void RenderLabel(HtmlTextWriter writer, string text, string clientID) {
            writer.AddAttribute(HtmlTextWriterAttribute.For, clientID);
 
            if (BinaryCompatibility.Current.TargetsAtLeastFramework48 && _labelAttributesState != null && _labelAttributesState.Count != 0) {
                LabelAttributes.AddAttributes(writer);
            } else if (_labelAttributes != null && _labelAttributes.Count != 0) {
                _labelAttributes.AddAttributes(writer);
            }
 
            writer.RenderBeginTag(HtmlTextWriterTag.Label);
            writer.Write(text);
            writer.RenderEndTag();
        }
 
        internal virtual void RenderInputTag(HtmlTextWriter writer, string clientID, string onClick) {
            if (clientID != null) {
                writer.AddAttribute(HtmlTextWriterAttribute.Id, clientID);
            }
 
            writer.AddAttribute(HtmlTextWriterAttribute.Type, "checkbox");
 
            if (UniqueID != null) {
                writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID);
            }
 
            // Whidbey 20815
            if (_valueAttribute != null) {
                writer.AddAttribute(HtmlTextWriterAttribute.Value, _valueAttribute);
            }
 
            if (Checked)
                writer.AddAttribute(HtmlTextWriterAttribute.Checked, "checked");
 
            // ASURT 119141: Render ---- attribute on the INPUT tag (instead of the SPAN) so the checkbox actually gets disabled when Enabled=false
            if (!IsEnabled && SupportsDisabledAttribute) {
                writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");
            }
 
            if (AutoPostBack && (Page != null) && Page.ClientSupportsJavaScript) {
 
                PostBackOptions options = new PostBackOptions(this, String.Empty);
 
                if (CausesValidation && Page.GetValidators(ValidationGroup).Count > 0) {
                    options.PerformValidation = true;
                    options.ValidationGroup = ValidationGroup;
                }
 
                if (Page.Form != null) {
                    options.AutoPostBack = true;
                }
 
                // ASURT 98368
                // Need to merge the autopostback script with the user script
                onClick = Util.MergeScript(onClick, Page.ClientScript.GetPostBackEventReference(options, true));
                writer.AddAttribute(HtmlTextWriterAttribute.Onclick, onClick);
 
                if (EnableLegacyRendering) {
                    writer.AddAttribute("language", "javascript", false);
                }
            }
            else {
                if (Page != null) {
                    Page.ClientScript.RegisterForEventValidation(this.UniqueID);
                }
 
                if (onClick != null) {
                    writer.AddAttribute(HtmlTextWriterAttribute.Onclick, onClick);
                }
            }
 
            string s = AccessKey;
            if (s.Length > 0)
                writer.AddAttribute(HtmlTextWriterAttribute.Accesskey, s);
 
            int i = TabIndex;
            if (i != 0)
                writer.AddAttribute(HtmlTextWriterAttribute.Tabindex, i.ToString(NumberFormatInfo.InvariantInfo));
 
            if (BinaryCompatibility.Current.TargetsAtLeastFramework48 && _inputAttributesState != null && _inputAttributesState.Count != 0) {
                InputAttributes.AddAttributes(writer);
            } else if (_inputAttributes != null && _inputAttributes.Count != 0) {
                _inputAttributes.AddAttributes(writer);
            }
 
            writer.RenderBeginTag(HtmlTextWriterTag.Input);
            writer.RenderEndTag();
        }
 
        /// <internalonly/>
        /// <devdoc>
        /// <para>Processes posted data for the <see cref='System.Web.UI.WebControls.CheckBox'/>
        /// control.</para>
        /// </devdoc>
        bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {
            return LoadPostData(postDataKey, postCollection);
        }
 
 
        /// <internalonly/>
        /// <devdoc>
        /// <para>Processes posted data for the <see cref='System.Web.UI.WebControls.CheckBox'/>
        /// control.</para>
        /// </devdoc>
        protected virtual bool LoadPostData(string postDataKey, NameValueCollection postCollection) {
            bool dataChanged = false;
 
            string post = postCollection[postDataKey];
 
            bool isChecked = (!String.IsNullOrEmpty(post));
 
            if (isChecked) {
                ValidateEvent(postDataKey);
            }
 
            dataChanged = (isChecked != Checked);
            Checked = isChecked;
 
            return dataChanged;
        }
 
 
        /// <internalonly/>
        /// <devdoc>
        /// Raises when posted data for a control has changed.
        /// </devdoc>
        void IPostBackDataHandler.RaisePostDataChangedEvent() {
            RaisePostDataChangedEvent();
        }
 
 
        /// <internalonly/>
        /// <devdoc>
        /// Raises when posted data for a control has changed.
        /// </devdoc>
        protected virtual void RaisePostDataChangedEvent() {
            if (AutoPostBack && !Page.IsPostBackEventControlRegistered) {
                // VSWhidbey 204824
                Page.AutoPostBackControl = this;
 
                if (CausesValidation) {
                    Page.Validate(ValidationGroup);
                }
            }
            OnCheckedChanged(EventArgs.Empty);
        }
    }
}