File: winforms\Managed\System\WinForms\Binding.cs
Project: ndp\fx\src\System.Windows.Forms.csproj (System.Windows.Forms)
//------------------------------------------------------------------------------
// <copyright file="Binding.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
namespace System.Windows.Forms {
 
    using System;
    using Microsoft.Win32;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.ComponentModel;
    using System.ComponentModel.Design;
    using System.Collections;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Security;
 
    /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Represents a simple binding of a value in a list
    ///       and the property of a control.
    ///    </para>
    /// </devdoc>
    [TypeConverterAttribute(typeof(ListBindingConverter))]
    public class Binding {
 
        // the two collection owners that this binding belongs to.
        private IBindableComponent control;
        private BindingManagerBase bindingManagerBase;
        
        private BindToObject bindToObject = null;
        
        private string propertyName = "";
 
        private PropertyDescriptor propInfo;
        private PropertyDescriptor propIsNullInfo;
        private EventDescriptor validateInfo;
        private TypeConverter propInfoConverter;
 
        private bool formattingEnabled = false;
 
        private bool bound = false;
        private bool modified = false;
 
        //Recursion guards
        private bool inSetPropValue = false;
        private bool inPushOrPull = false;
        private bool inOnBindingComplete = false;
 
        // formatting stuff
        private string formatString = String.Empty;
        private IFormatProvider formatInfo = null;
        private object nullValue = null;
        private object dsNullValue = Formatter.GetDefaultDataSourceNullValue(null);
        private bool dsNullValueSet;
        private ConvertEventHandler onParse = null;
        private ConvertEventHandler onFormat = null;
 
        // binding stuff
        private ControlUpdateMode controlUpdateMode = ControlUpdateMode.OnPropertyChanged;
        private DataSourceUpdateMode dataSourceUpdateMode = DataSourceUpdateMode.OnValidation;
        private BindingCompleteEventHandler onComplete = null;
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Binding"]/*' />
        /// <devdoc>
        ///     Initializes a new instance of the <see cref='System.Windows.Forms.Binding'/> class
        ///     that binds a property on the owning control to a property on a data source.
        /// </devdoc>
        public Binding(string propertyName, Object dataSource, string dataMember) : this(propertyName, dataSource, dataMember, false, 0, null, String.Empty, null) {
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Binding6"]/*' />
        public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled) : this(propertyName, dataSource, dataMember, formattingEnabled, 0, null, String.Empty, null) {
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Binding2"]/*' />
        public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, String.Empty, null) {
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Binding3"]/*' />
        public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, String.Empty, null) {
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Binding5"]/*' />
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'formatString' is an appropriate name, since its a string passed to the Format method
        public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, null) {
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Binding4"]/*' />
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] // By design (no-one should be subclassing this class)
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters")] // 'formatString' is an appropriate name, since its a string passed to the Format method
        public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo) {
            this.bindToObject = new BindToObject(this, dataSource, dataMember);
 
            this.propertyName = propertyName;
            this.formattingEnabled = formattingEnabled;
            this.formatString = formatString;
            this.nullValue = nullValue;
            this.formatInfo = formatInfo;
            this.formattingEnabled = formattingEnabled;
            this.dataSourceUpdateMode = dataSourceUpdateMode;
 
            CheckBinding();
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Binding1"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Initializes a new instance of the <see cref='System.Windows.Forms.Binding'/> class.
        ///    </para>
        /// </devdoc>
        private Binding() {
        }
 
        internal BindToObject BindToObject {
            get {
                return this.bindToObject;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.DataSource"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public object DataSource {
            get {
                return this.bindToObject.DataSource;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.BindingMemberInfo"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public BindingMemberInfo BindingMemberInfo {
            get {
                return this.bindToObject.BindingMemberInfo;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.BindableComponent"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the control to which the binding belongs.
        ///    </para>
        /// </devdoc>
        [
        DefaultValue(null)
        ]
        public IBindableComponent BindableComponent {
            [
                SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)
            ]
            get {
                return this.control;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Control"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the control to which the binding belongs.
        ///    </para>
        /// </devdoc>
        [
        DefaultValue(null)
        ]
        public Control Control {
            [
                SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)
            ]
            get {
                return this.control as Control;
            }
        }
 
        // Is the binadable component in a 'created' (ready-to-use) state? For controls,
        // this depends on whether the window handle has been created yet. For everything
        // else, we'll assume they are always in a created state.
        internal static bool IsComponentCreated(IBindableComponent component) {
            Control ctrl = (component as Control);
 
            if (ctrl != null) {
                return ctrl.Created;
            }
            else {
                return true;
            }
        }
 
        // Instance-specific property equivalent to the static method above
        internal bool ComponentCreated {
            get {
                return IsComponentCreated(this.control);
            }
        }
 
        private void FormLoaded(object sender, EventArgs e) {
            Debug.Assert(sender == control, "which other control can send us the Load event?");
            // update the binding
            CheckBinding();
        }
 
        internal void SetBindableComponent(IBindableComponent value) {
            if (this.control != value) {
                IBindableComponent oldTarget = control;
                BindTarget(false);
                this.control = value;
                BindTarget(true);
                try {
                    CheckBinding();
                }
                catch {
                    BindTarget(false);
                    control = oldTarget;
                    BindTarget(true);
                    throw;
                }
 
                // We are essentially doing to the listManager what we were doing to the
                // BindToObject: bind only when the control is created and it has a BindingContext
                BindingContext.UpdateBinding((control != null && IsComponentCreated(control) ? control.BindingContext: null), this);
                Form form = value as Form;
                if (form != null) {
                    form.Load += new EventHandler(FormLoaded);
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.IsBinding"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets a value indicating whether the binding is active.
        ///    </para>
        /// </devdoc>
        public bool IsBinding {
            get {
                return bound;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.BindingManagerBase"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets the <see cref='System.Windows.Forms.BindingManagerBase'/>
        ///       of this binding that allows enumeration of a set of
        ///       bindings.
        ///    </para>
        /// </devdoc>
        public BindingManagerBase BindingManagerBase{
            get {
                return bindingManagerBase;
            }
        }
 
        internal void SetListManager(BindingManagerBase bindingManagerBase) {
            if (this.bindingManagerBase is CurrencyManager) {
                ((CurrencyManager)this.bindingManagerBase).MetaDataChanged -= new EventHandler(binding_MetaDataChanged);
            }
 
            this.bindingManagerBase = bindingManagerBase;
 
            if (this.bindingManagerBase is CurrencyManager ) {
                ((CurrencyManager)this.bindingManagerBase).MetaDataChanged += new EventHandler(binding_MetaDataChanged);
            }
 
            this.BindToObject.SetBindingManagerBase(bindingManagerBase);
            CheckBinding();
        }
        
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.PropertyName"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Gets or sets the property on the control to bind to.
        ///    </para>
        /// </devdoc>
        [DefaultValue("")]
        public string PropertyName {
            get {
                return propertyName;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.BindingComplete"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event BindingCompleteEventHandler BindingComplete {
            add {
                onComplete += value;
            }
            remove {
                onComplete -= value;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Parse"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event ConvertEventHandler Parse {
            add {
                onParse += value;
            }
            remove {
                onParse -= value;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.Format"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public event ConvertEventHandler Format {
            add {
                onFormat += value;
            }
            remove {
                onFormat -= value;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.FormattingEnabled"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [DefaultValue(false)]
        public bool FormattingEnabled {
 
            // A note about FormattingEnabled: This flag was introduced in Whidbey, to enable new
            // formatting features. However, it is also used to trigger other new Whidbey binding
            // behavior not related to formatting (such as error handling). This preserves Everett
            // legacy behavior for old bindings (where FormattingEnabled = false).
 
            get {
                return formattingEnabled;
            }
            set {
                if (formattingEnabled != value) {
                    formattingEnabled = value;
                    if (IsBinding) {
                        PushData();
                    }
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.FormatInfo"]/*' />
        [DefaultValue(null)]
        public IFormatProvider FormatInfo {
            get {
                return this.formatInfo;
            }
            set {
                if (formatInfo != value) {
                    this.formatInfo = value;
                    if (IsBinding) {
                        PushData();
                    }
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.FormatString"]/*' />
        public string FormatString {
            get {
                return this.formatString;
            }
            set {
                if (value == null)
                    value = String.Empty;
                if (!value.Equals(formatString)) {
                    this.formatString = value;
                    if (IsBinding) {
                        PushData();
                    }
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.NullValue"]/*' />
        public object NullValue {
            get {
                return this.nullValue;
            }
            set {
                // Try to compare logical values, not object references...
                if (!Object.Equals(nullValue, value)) {
                    this.nullValue = value;
 
                    // If data member is currently DBNull, force update of bound
                    // control property so that it displays the new NullValue
                    //
                    if (IsBinding && Formatter.IsNullData(bindToObject.GetValue(), this.dsNullValue)) {
                        PushData();
                    }
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.DataSourceNullValue"]/*' />
        public object DataSourceNullValue {
            get {
                return this.dsNullValue;
            }
            set {
                // Try to compare logical values, not object references...
                if (!Object.Equals(this.dsNullValue, value)) {
 
                    // Save old Value
                    object oldValue = this.dsNullValue;
 
                    // Set value
                    this.dsNullValue = value;
 
                    this.dsNullValueSet = true;
 
                    // If control's property is capable of displaying a special value for DBNull,
                    // and the DBNull status of data source's property has changed, force the
                    // control property to refresh itself from the data source property.
                    //
                    if (IsBinding) {
                        object dsValue = bindToObject.GetValue();
 
                        // Check previous DataSourceNullValue for null
                        if (Formatter.IsNullData(dsValue, oldValue)) {
                            // Update DataSource Value to new DataSourceNullValue
                            WriteValue();
                        }
 
                        // Check current DataSourceNullValue
                        if (Formatter.IsNullData(dsValue, value)) {
                            // Update Control because the DataSource is now null
                            ReadValue();
                        }
                    }
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.ControlUpdateMode"]/*' />
        [DefaultValue(ControlUpdateMode.OnPropertyChanged)]
        public ControlUpdateMode ControlUpdateMode {
            get {
                return this.controlUpdateMode;
            }
            set {
                if (this.controlUpdateMode != value) {
                    this.controlUpdateMode = value;
 
                    // Refresh the control from the data source, to reflect the new update mode
                    if (IsBinding) {
                        PushData();
                    }
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.DataSourceUpdateMode"]/*' />
        [DefaultValue(DataSourceUpdateMode.OnValidation)]
        public DataSourceUpdateMode DataSourceUpdateMode {
            get {
                return this.dataSourceUpdateMode;
            }
            set {
                if (this.dataSourceUpdateMode != value) {
                    this.dataSourceUpdateMode = value;
                }
            }
        }
 
        private void BindTarget(bool bind) {
            if (bind) {
                if (IsBinding) {
                    if (propInfo != null && control != null) {
                        EventHandler handler = new EventHandler(this.Target_PropertyChanged);
                        propInfo.AddValueChanged(control, handler);
                    }
                    if (validateInfo != null) {
                        CancelEventHandler handler = new CancelEventHandler(this.Target_Validate);
                        validateInfo.AddEventHandler(control, handler);
                    }
                }
            }
            else {
                if (propInfo != null && control != null) {
                    EventHandler handler = new EventHandler(this.Target_PropertyChanged);
                    propInfo.RemoveValueChanged(control, handler);
                }
                if (validateInfo != null) {
                    CancelEventHandler handler = new CancelEventHandler(this.Target_Validate);
                    validateInfo.RemoveEventHandler(control, handler);
                }               
            }
        }
 
        private void binding_MetaDataChanged(object sender, EventArgs e) {
            Debug.Assert(sender == this.bindingManagerBase, "we should only receive notification from our binding manager base");
            CheckBinding();
        }
 
        private void CheckBinding() {
            this.bindToObject.CheckBinding();
 
            if (control != null && propertyName.Length > 0) {
                control.DataBindings.CheckDuplicates(this);
                
                Type controlClass = control.GetType();
 
                // Check Properties
                string propertyNameIsNull = propertyName + "IsNull";
                Type propType = null;
                PropertyDescriptor tempPropInfo = null;
                PropertyDescriptor tempPropIsNullInfo = null;
                PropertyDescriptorCollection propInfos;
                 
                // If the control is being inherited, then get the properties for
                // the control's type rather than for the control itself.  Getting
                // properties for the control will merge the control's properties with
                // those of its designer.  Normally we want that, but for 
                // inherited controls we don't because an inherited control should 
                // "act" like a runtime control.
                //
                InheritanceAttribute attr = (InheritanceAttribute)TypeDescriptor.GetAttributes(control)[typeof(InheritanceAttribute)];
                if (attr != null && attr.InheritanceLevel != InheritanceLevel.NotInherited) {
                    propInfos = TypeDescriptor.GetProperties(controlClass);
                }
                else {
                    propInfos = TypeDescriptor.GetProperties(control);
                }
                
                for (int i = 0; i < propInfos.Count; i++) {
                    if(tempPropInfo==null && String.Equals (propInfos[i].Name, propertyName, StringComparison.OrdinalIgnoreCase)) {
                        tempPropInfo = propInfos[i];
                        if (tempPropIsNullInfo != null)
                            break;
                    }
                    if(tempPropIsNullInfo == null && String.Equals (propInfos[i].Name, propertyNameIsNull, StringComparison.OrdinalIgnoreCase)) {
                        tempPropIsNullInfo = propInfos[i];
                        if (tempPropInfo != null)
                            break;
                    }
                }
 
                if (tempPropInfo == null) {
                    throw new ArgumentException(SR.GetString(SR.ListBindingBindProperty, propertyName), "PropertyName");
                }
                if (tempPropInfo.IsReadOnly && this.controlUpdateMode != ControlUpdateMode.Never) {
                    throw new ArgumentException(SR.GetString(SR.ListBindingBindPropertyReadOnly, propertyName), "PropertyName");
                }
 
                propInfo = tempPropInfo;
                propType = propInfo.PropertyType;
                propInfoConverter = propInfo.Converter;
 
                if (tempPropIsNullInfo != null && tempPropIsNullInfo.PropertyType == typeof(bool) && !tempPropIsNullInfo.IsReadOnly)
                    propIsNullInfo = tempPropIsNullInfo;
 
                // Check events
                EventDescriptor tempValidateInfo = null;
                string validateName = "Validating";
                EventDescriptorCollection eventInfos = TypeDescriptor.GetEvents(control);
                for (int i = 0; i < eventInfos.Count; i++) {
                    if(tempValidateInfo==null && String.Equals (eventInfos[i].Name, validateName, StringComparison.OrdinalIgnoreCase)) {
                        tempValidateInfo = eventInfos[i];
                        break;
                    }
                }
                validateInfo = tempValidateInfo;
            }
            else {
                propInfo = null;
                validateInfo = null;
            }
 
            // go see if we become bound now.
            UpdateIsBinding();
        }
 
        internal bool ControlAtDesignTime() {
            IComponent comp = (this.control as IComponent);
            if (comp == null)
                return false;
 
            ISite site = comp.Site;
            if (site == null)
                return false;
 
            return site.DesignMode;
        }
 
        private object GetDataSourceNullValue(Type type) {
            return this.dsNullValueSet ? this.dsNullValue : Formatter.GetDefaultDataSourceNullValue(type);
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")] // Perfectly acceptible when dealing with PropertyDescriptors
        private Object GetPropValue() {
            bool isNull = false;
            if (propIsNullInfo != null) {
                isNull = (bool) propIsNullInfo.GetValue(control);
            }
            Object value;
            if (isNull) {
                value = DataSourceNullValue;
            }
            else {
                value =  propInfo.GetValue(control);
                // bug 92443: the code before was changing value to DBNull if the value
                // was the empty string. we can't do this, because we need to format the value
                // in the property in the control and then push it back into the control.
                if (value == null) {
                    value = DataSourceNullValue;
                }
            }
            return value;
        }
 
        private BindingCompleteEventArgs CreateBindingCompleteEventArgs(BindingCompleteContext context, Exception ex) {
            bool cancel = false;
            string errorText = String.Empty;
            BindingCompleteState state = BindingCompleteState.Success;
 
            if (ex != null) {
                // If an exception was provided, report that
                errorText = ex.Message;
                state = BindingCompleteState.Exception;
                cancel = true;
            }
            else {
                // If data error info on data source for this binding, report that
                errorText = this.BindToObject.DataErrorText;
 
                // We should not cancel with an IDataErrorInfo error - we didn't in Everett
                if (!String.IsNullOrEmpty(errorText)) {
                    state = BindingCompleteState.DataError;
                }
            }
 
            return new BindingCompleteEventArgs(this, state, context, errorText, ex, cancel);
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.OnBindingComplete"]/*' />
        protected virtual void OnBindingComplete(BindingCompleteEventArgs e) {
            // This recursion guard will only be in effect if FormattingEnabled because this method
            // is only called if formatting is enabled.
            if (!inOnBindingComplete) {
                try {
                    inOnBindingComplete = true;
                    if (onComplete != null) {
                        onComplete(this, e);
                    }
                }
                catch (Exception ex) {
                    if (ClientUtils.IsSecurityOrCriticalException(ex)) {
                        throw;
                    }
 
                    // Fix for VSWhidbey#303304. BindingComplete event is intended primarily as an "FYI" event with support for cancellation.
                    // User code should not be throwing exceptions from this event as a way to signal new error conditions (they should use
                    // things like the Format or Parse events for that). Exceptions thrown here can mess up currency manager behavior big time.
                    // For now, eat any non-critical exceptions and instead just cancel the current push/pull operation.
                    e.Cancel = true;
                }
                finally {
                    inOnBindingComplete = false;
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.OnParse"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected virtual void OnParse(ConvertEventArgs cevent) {
            if (onParse != null) {
                onParse(this, cevent);
            }
 
            if (!formattingEnabled) {
                if (!(cevent.Value is System.DBNull) && cevent.Value != null && cevent.DesiredType != null && !cevent.DesiredType.IsInstanceOfType(cevent.Value) && (cevent.Value is IConvertible)) {
                    cevent.Value = Convert.ChangeType(cevent.Value, cevent.DesiredType, CultureInfo.CurrentCulture);
                }
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.OnFormat"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        protected virtual void OnFormat(ConvertEventArgs cevent) {
            if (onFormat!= null) {
                onFormat(this, cevent);
            }
            if (!formattingEnabled) {
                if (!(cevent.Value is System.DBNull) && cevent.DesiredType != null && !cevent.DesiredType.IsInstanceOfType(cevent.Value) && (cevent.Value is IConvertible)) {
                    cevent.Value = Convert.ChangeType(cevent.Value, cevent.DesiredType, CultureInfo.CurrentCulture);
                }
            }
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        private object ParseObject(object value) {
            Type type = this.bindToObject.BindToType;
 
            if (formattingEnabled) {
                // -------------------------------
                // Behavior for Whidbey and beyond
                // -------------------------------
 
                // Fire the Parse event so that user code gets a chance to supply the parsed value for us
                ConvertEventArgs e = new ConvertEventArgs(value, type);
                OnParse(e);
 
                object newValue = e.Value;
 
                if (!object.Equals(value, newValue)) {
                    // If event handler replaced formatted value with parsed value, use that
                    return newValue;
                }
                else {
                    // Otherwise parse the formatted value ourselves
                    TypeConverter fieldInfoConverter = null;
                    if (bindToObject.FieldInfo != null) {
                        fieldInfoConverter = bindToObject.FieldInfo.Converter;
                    }
                    return Formatter.ParseObject(value, type, (value == null ? propInfo.PropertyType : value.GetType()), fieldInfoConverter, propInfoConverter, formatInfo, nullValue, GetDataSourceNullValue(type));
                }
 
            } else {
                // ----------------------------
                // Behavior for RTM and Everett  [DO NOT MODIFY!]
                // ----------------------------
 
                ConvertEventArgs e = new ConvertEventArgs(value, type);
                // first try: use the OnParse event
                OnParse(e);
                // bug 75825: if the user choose to push a null in to the back end, then we should push it like it is.
                if (e.Value != null && (e.Value.GetType().IsSubclassOf(type) || e.Value.GetType() == type || e.Value is System.DBNull))
                    return e.Value;
                // second try: use the TypeConverter
                TypeConverter typeConverter = TypeDescriptor.GetConverter(value != null ? value.GetType() : typeof(Object));
                if (typeConverter != null && typeConverter.CanConvertTo(type)) {
                    return typeConverter.ConvertTo(value, type);
                }
                // last try: use Convert.ToType
                if (value is IConvertible) {
                    object ret = Convert.ChangeType(value, type, CultureInfo.CurrentCulture);
                    if (ret != null && (ret.GetType().IsSubclassOf(type) || ret.GetType() == type))
                        return ret;
                }
                // time to fail: (RTM/Everett just returns null, whereas Whidbey throws an exception)
                return null;
            }
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        private object FormatObject(object value) {
            // We will not format the object when the control is in design time.
            // This is because if we bind a boolean property on a control to a
            // row that is full of DBNulls then we cause problems in the shell.
            if (ControlAtDesignTime())
                return value;
 
            Type type = propInfo.PropertyType;
 
            if (formattingEnabled) {
                // -------------------------------
                // Behavior for Whidbey and beyond
                // -------------------------------
 
                // Fire the Format event so that user code gets a chance to supply the formatted value for us
                ConvertEventArgs e = new ConvertEventArgs(value, type);
                OnFormat(e);
 
                if (e.Value != value) {
                    // If event handler replaced parsed value with formatted value, use that
                    return e.Value;
                }
                else {
                    // Otherwise format the parsed value ourselves
                    TypeConverter fieldInfoConverter = null;
                    if (bindToObject.FieldInfo != null) {
                        fieldInfoConverter = bindToObject.FieldInfo.Converter;
                    }
                    return Formatter.FormatObject(value, type, fieldInfoConverter, propInfoConverter, formatString, formatInfo, nullValue, dsNullValue);
                }
 
            } else {
                // ----------------------------
                // Behavior for RTM and Everett  [DO NOT MODIFY!]
                // ----------------------------
 
                // first try: use the Format event
                ConvertEventArgs e = new ConvertEventArgs(value, type);
                OnFormat(e);
                object ret = e.Value;
 
                // Approved breaking-change behavior between RTM and Everett: Fire the Format event even if the control property is of type
                // Object (RTM used to skip the event for properties of this type). NOTE: This change contains a bug (fixed in the new
                // Whidbey logic above); Everett always returns the *original* object in this case, ignoring any attempt by the event handler
                // to replace this with a different object.
                if (type == typeof(object))
                    return value;
 
                // stop now if we have a value of a compatible type
                if (ret != null && (ret.GetType().IsSubclassOf(type) || ret.GetType() == type))
                    return ret;
                // second try: use type converter for the desiredType
                TypeConverter typeConverter = TypeDescriptor.GetConverter(value != null ? value.GetType() : typeof(Object));
                if (typeConverter != null && typeConverter.CanConvertTo(type)) {
                    ret = typeConverter.ConvertTo(value, type);
                    return ret;
                }
                // last try: use Convert.ChangeType
                if (value is IConvertible) {
                    ret = Convert.ChangeType(value, type, CultureInfo.CurrentCulture);
                    if (ret != null && (ret.GetType().IsSubclassOf(type) || ret.GetType() == type))
                        return ret;
                }
 
                // time to fail:
                throw new FormatException(SR.GetString(SR.ListBindingFormatFailed));
            }
        }
 
        //
        // PullData()
        //
        // Pulls data from control property into data source. Returns bool indicating whether caller
        // should cancel the higher level operation. Raises a BindingComplete event regardless of
        // success or failure.
        //
        // When the user leaves the control, it will raise a Validating event, calling the Binding.Target_Validate
        // method, which in turn calls PullData. PullData is also called by the binding manager when pulling data
        // from all bounds properties in one go.
        //
 
        internal bool PullData() {
            return PullData(true, false);
        }
 
        internal bool PullData(bool reformat) {
            return PullData(reformat, false);
        }
 
        internal bool PullData(bool reformat, bool force) {
            //Don't update the control if the control update mode is never.
            if (ControlUpdateMode == ControlUpdateMode.Never) {
                reformat = false;
            }
            bool parseFailed = false;
            object parsedValue = null;
            Exception lastException = null;
 
            // Check whether binding has been suspended or is simply not possible right now
            if (!IsBinding) {
                return false;
            }
 
            // If caller is not FORCING us to pull, determine whether we want to pull right now...
            if (!force) {
                // If control property supports change events, only pull if the value has been changed since
                // the last update (ie. its dirty). For properties that do NOT support change events, we cannot
                // track the dirty state, so we just have to pull all the time.
                if (propInfo.SupportsChangeEvents && !modified) {
                    return false;
                }
 
                // Don't pull if the update mode is 'Never' (ie. read-only binding)
                if (DataSourceUpdateMode == DataSourceUpdateMode.Never) {
                    return false;
                }
            }
 
            // Re-entrancy check between push and pull (new for Whidbey - requires FormattingEnabled)
            if (inPushOrPull && formattingEnabled) {
                return false;
            }
 
            inPushOrPull = true;
 
            // Get the value from the bound control property
            Object value = GetPropValue();
 
            // Attempt to parse the property value into a format suitable for the data source
            try {
                parsedValue = ParseObject(value);
            }
            catch (Exception ex) {
                lastException = ex;
 
                // ...pre-Whidbey behavior was to eat parsing exceptions. This behavior is preserved.
            }
 
            try {
                // If parse failed, reset control property value back to original data source value.
                // An exception always indicates a parsing failure. A parsed value of null only indicates
                // a parsing failure when following pre-Whidbey behavior (ie. FormattingEnabled=False) since
                // in Whidbey we now support writing null back to the data source (eg. for business objects).
                if (lastException != null || (!FormattingEnabled && parsedValue == null)) {
                    parseFailed = true;
                    parsedValue = this.bindToObject.GetValue();
                }
 
                // Format the parsed value to be re-displayed in the control
                if (reformat) {
                    if (FormattingEnabled && parseFailed) {
                        // New behavior for Whidbey (ie. requires FormattingEnabled=true). If parsing
                        // fails, do NOT push the original data source value back into the control.
                        // This blows away the invalid value before the user gets a chance to see
                        // what needs correcting, which was the Everett behavior.
                    }
                    else {
                        object formattedObject = FormatObject(parsedValue);
 
                        // New behavior for Whidbey (Bug#194609). Do not push the re-formatted
                        // value into the control if it is identical to the current formatted
                        // value unless we're forced to (thereby avoiding unnecessary property sets).
                        if (force || !FormattingEnabled || !Object.Equals(formattedObject, value)) {
                            SetPropValue(formattedObject);
                        }
                    }
                }
 
                // Put the value into the data model
                if (!parseFailed) {
                    this.bindToObject.SetValue(parsedValue);
                }
            }
            catch (Exception ex) {
                lastException = ex;
 
                // This try/catch is new for Whidbey. To preserve Everett behavior, re-throw the
                // exception unless this binding has formatting enabled (new Whidbey feature).
                if (!FormattingEnabled) {
                    throw;
                }
            }
            finally {
                inPushOrPull = false;
            }
 
            if (FormattingEnabled) {
                // Whidbey...
 
                // Raise the BindingComplete event, giving listeners a chance to process any
                // errors that occured and decide whether the operation should be cancelled.
                BindingCompleteEventArgs args = CreateBindingCompleteEventArgs(BindingCompleteContext.DataSourceUpdate, lastException);
                OnBindingComplete(args);
 
                // If the operation completed successfully (and was not cancelled), we can clear the dirty flag
                // on this binding because we know the value in the control was valid and has been accepted by
                // the data source. But if the operation failed (or was cancelled), we must leave the dirty flag
                // alone, so that the control's value will continue to be re-validated and re-pulled later.
                if (args.BindingCompleteState == BindingCompleteState.Success && args.Cancel == false)
                    modified = false;
 
                return args.Cancel;
            }
            else {
                // Everett...
 
                // Do not emit BindingComplete events, or allow the operation to be cancelled.
                // If we get this far, treat the operation as successful and clear the dirty flag.
                modified = false;
                return false;
            }
        }
 
        //
        // PushData()
        //
        // Pushes data from data source into control property. Returns bool indicating whether caller
        // should cancel the higher level operation. Raises a BindingComplete event regardless of
        // success or failure.
        //
 
        internal bool PushData() {
            return PushData(false);
        }
 
        internal bool PushData(bool force) {
            Object dataSourceValue = null;
            Exception lastException = null;
 
            // Don't push if update mode is 'Never' (unless caller is FORCING us to push)
            if (!force && ControlUpdateMode == ControlUpdateMode.Never) {
                return false;
            }
 
            // Re-entrancy check between push and pull (new for Whidbey - requires FormattingEnabled)
            if (inPushOrPull && formattingEnabled) {
                return false;
            }
 
            inPushOrPull = true;
 
            try {
                if (IsBinding) {
                    dataSourceValue = bindToObject.GetValue();
                    object controlValue = FormatObject(dataSourceValue);
                    SetPropValue(controlValue);
                    modified = false;
                }
                else {
                    SetPropValue(null);
                }
            }
            catch (Exception ex) {
                lastException = ex;
 
                // This try/catch is new for Whidbey. To preserve Everett behavior, re-throw the
                // exception unless this binding has formatting enabled (new Whidbey feature).
                if (!FormattingEnabled) {
                    throw;
                }
            }
            finally {
                inPushOrPull = false;
            }
 
            if (FormattingEnabled) {
                // Whidbey...
 
                // Raise the BindingComplete event, giving listeners a chance to process any errors that occured, and decide
                // whether the operation should be cancelled. But don't emit the event if we didn't actually update the control.
                BindingCompleteEventArgs args = CreateBindingCompleteEventArgs(BindingCompleteContext.ControlUpdate, lastException);
                OnBindingComplete(args);
 
                return args.Cancel;
            }
            else {
                // Everett...
 
                // Do not emit BindingComplete events, or allow the operation to be cancelled.
                return false;
            }
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.ReadValue"]/*' />
        /// <devdoc>
        ///     Reads current value from data source, and sends this to the control.
        /// </devdoc>
        public void ReadValue() {
            PushData(/*force:*/ true);
        }
 
        /// <include file='doc\ListBinding.uex' path='docs/doc[@for="Binding.WriteValue"]/*' />
        /// <devdoc>
        ///     Takes current value from control, and writes this out to the data source.
        /// </devdoc>
        public void WriteValue() {
            // PullData has a guard for ControlUpdateMode == Never.
 
            PullData(/*reformat:*/ true, /*force:*/ true);
        }
 
        private void SetPropValue(Object value) {
            // we will not pull the data from the back end into the control
            // when the control is in design time. this is because if we bind a boolean property on a control
            // to a row that is full of DBNulls then we cause problems in the shell.
            if (ControlAtDesignTime())
                return;
 
            inSetPropValue = true;
 
            try {
                bool isNull = value == null || Formatter.IsNullData(value, DataSourceNullValue);
                if (isNull) {
                    if (propIsNullInfo != null) {
                        propIsNullInfo.SetValue(control, true);
                    }
                    else {
                        if (propInfo.PropertyType == typeof(object)) {
                            propInfo.SetValue(control, DataSourceNullValue);
                        }
                        else {
                            propInfo.SetValue(control, null);
                        }
 
                    }
                }
                else {
                    propInfo.SetValue(control, value);
                }
            }
            finally {
                inSetPropValue = false;
            }
        }
 
        private bool ShouldSerializeFormatString() {
            return formatString != null && formatString.Length > 0;
 
        }
 
        private bool ShouldSerializeNullValue() {
            return nullValue != null;
        }
 
        private bool ShouldSerializeDataSourceNullValue() {
            return this.dsNullValueSet && this.dsNullValue != Formatter.GetDefaultDataSourceNullValue(null);
        }
 
        private void Target_PropertyChanged(Object sender, EventArgs e) {
            if (inSetPropValue)
                return;
 
            if (IsBinding) {
                //dataSource.BeginEdit();
 
                modified = true;
 
                // If required, update data source every time control property changes.
                //      NOTE: We need modified=true to be set both before pulling data
                //      (so that pull will work) and afterwards (so that validation will
                //      still occur later on).
                if (DataSourceUpdateMode == DataSourceUpdateMode.OnPropertyChanged) {
                    PullData(false); // false = don't reformat property (bad user experience!!)
                    modified = true;
                }
            }
        }
 
        // Event handler for the Control.Validating event on the control that we are bound to.
        //
        // If value in control has changed, we want to send that value back up to the data source
        // when the control undergoes validation (eg. on loss of focus). If an error occurs, we
        // will set e.Cancel=true to make validation fail and force focus to remain on the control.
        //
        // NOTE: If no error occurs, we MUST leave e.Cancel alone, to respect any value put in there
        // by event handlers high up the event chain.
        //
        private void Target_Validate(Object sender, CancelEventArgs e) {
            try {
                if (PullData(true)) {
                    e.Cancel = true;
                }
            }
            catch {
                e.Cancel = true;
            }
        }
 
        internal bool IsBindable {
            get {
                return (control != null && propertyName.Length > 0 &&
                                bindToObject.DataSource != null && bindingManagerBase != null);
            }
        }
        
        internal void UpdateIsBinding() {
            bool newBound =  IsBindable && ComponentCreated && bindingManagerBase.IsBinding;
            if (bound != newBound) {
                bound = newBound;
                BindTarget(newBound);
                if (bound) {
                    if (controlUpdateMode == ControlUpdateMode.Never) {
                        PullData(false, true); //Don't reformat, force pull
                    } 
                    else {
                        PushData();
                    }
                }
            }
        }
    }
}