Code:
/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / WinForms / Managed / System / WinForms / Binding.cs / 1 / Binding.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- 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; ////// /// [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; ////// Represents a simple binding of a value in a list /// and the property of a control. /// ////// /// Initializes a new instance of the public Binding(string propertyName, Object dataSource, string dataMember) : this(propertyName, dataSource, dataMember, false, 0, null, String.Empty, null) { } ///class /// that binds a property on the owning control to a property on a data source. /// public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled) : this(propertyName, dataSource, dataMember, formattingEnabled, 0, null, String.Empty, null) { } /// public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, String.Empty, null) { } /// public Binding(string propertyName, Object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue) : this(propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, String.Empty, null) { } /// [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) { } /// [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(); } /// /// /// private Binding() { } internal BindToObject BindToObject { get { return this.bindToObject; } } ////// Initializes a new instance of the ///class. /// /// /// public object DataSource { get { return this.bindToObject.DataSource; } } ///[To be supplied.] ////// /// public BindingMemberInfo BindingMemberInfo { get { return this.bindToObject.BindingMemberInfo; } } ///[To be supplied.] ////// /// [ DefaultValue(null) ] public IBindableComponent BindableComponent { [ SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode) ] get { return this.control; } } ////// Gets the control to which the binding belongs. /// ////// /// [ 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); } } } ////// Gets the control to which the binding belongs. /// ////// /// public bool IsBinding { get { return bound; } } ////// Gets a value indicating whether the binding is active. /// ////// /// 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(); } ////// Gets the ////// of this binding that allows enumeration of a set of /// bindings. /// /// /// [DefaultValue("")] public string PropertyName { get { return propertyName; } } ////// Gets or sets the property on the control to bind to. /// ////// /// public event BindingCompleteEventHandler BindingComplete { add { onComplete += value; } remove { onComplete -= value; } } ///[To be supplied.] ////// /// public event ConvertEventHandler Parse { add { onParse += value; } remove { onParse -= value; } } ///[To be supplied.] ////// /// public event ConvertEventHandler Format { add { onFormat += value; } remove { onFormat -= value; } } ///[To be supplied.] ////// /// [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(); } } } } ///[To be supplied.] ///[DefaultValue(null)] public IFormatProvider FormatInfo { get { return this.formatInfo; } set { if (formatInfo != value) { this.formatInfo = value; if (IsBinding) { PushData(); } } } } /// public string FormatString { get { return this.formatString; } set { if (value == null) value = String.Empty; if (!value.Equals(formatString)) { this.formatString = value; if (IsBinding) { PushData(); } } } } /// 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(); } } } } /// 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(); } } } } } /// [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(); } } } } /// [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); } /// 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; } catch { e.Cancel = true; } finally { inOnBindingComplete = false; } } } /// /// /// 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); } } } ///[To be supplied.] ////// /// 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); } } } ///[To be supplied.] ////// 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; } } ///[To be supplied.] ////// 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; } } ///[To be supplied.] ////// /// Reads current value from data source, and sends this to the control. /// public void ReadValue() { PushData(/*force:*/ true); } ////// /// Takes current value from control, and writes this out to the data source. /// 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(); } } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RelatedView.cs
- LambdaCompiler.Address.cs
- _SslSessionsCache.cs
- WebBrowserEvent.cs
- AttachInfo.cs
- XsltSettings.cs
- BypassElementCollection.cs
- DtdParser.cs
- ErrorFormatterPage.cs
- ArrangedElement.cs
- objectquery_tresulttype.cs
- XhtmlTextWriter.cs
- HtmlGenericControl.cs
- WorkflowInstanceUnhandledExceptionRecord.cs
- QilChoice.cs
- XPathAncestorQuery.cs
- XmlProcessingInstruction.cs
- MexServiceChannelBuilder.cs
- Int32CollectionConverter.cs
- WindowsTooltip.cs
- DataContractJsonSerializer.cs
- serverconfig.cs
- PeekCompletedEventArgs.cs
- MetabaseServerConfig.cs
- ColorConvertedBitmap.cs
- HttpWebResponse.cs
- GlobalizationAssembly.cs
- PageStatePersister.cs
- Timer.cs
- NetStream.cs
- FlatButtonAppearance.cs
- BrowserCapabilitiesFactoryBase.cs
- ADMembershipUser.cs
- smtpconnection.cs
- DeferredReference.cs
- PathStreamGeometryContext.cs
- WeakReadOnlyCollection.cs
- VectorCollection.cs
- SafeRightsManagementHandle.cs
- NavigationService.cs
- WindowsAltTab.cs
- CorrelationTokenTypeConvertor.cs
- CursorConverter.cs
- BufferedMessageWriter.cs
- Types.cs
- TextDataBindingHandler.cs
- ClaimSet.cs
- StrongName.cs
- exports.cs
- XmlSecureResolver.cs
- GridToolTip.cs
- SystemInformation.cs
- CryptoKeySecurity.cs
- RelationHandler.cs
- mil_commands.cs
- StringReader.cs
- SmtpTransport.cs
- SQLGuid.cs
- ScrollBarRenderer.cs
- CachingHintValidation.cs
- SystemKeyConverter.cs
- TextParentUndoUnit.cs
- PopupRoot.cs
- SpellerHighlightLayer.cs
- EventlogProvider.cs
- ContainerSelectorGlyph.cs
- FixedPosition.cs
- InvalidProgramException.cs
- ExecutionEngineException.cs
- RemoteWebConfigurationHostServer.cs
- ToolBarPanel.cs
- RootAction.cs
- StartUpEventArgs.cs
- WebBrowser.cs
- JavaScriptString.cs
- RadioButtonList.cs
- Publisher.cs
- MappedMetaModel.cs
- DrawingState.cs
- WaitHandle.cs
- AdapterUtil.cs
- ScaleTransform3D.cs
- GACMembershipCondition.cs
- PairComparer.cs
- ErrorProvider.cs
- TextBounds.cs
- TypeUtils.cs
- ToolbarAUtomationPeer.cs
- ToolStripStatusLabel.cs
- UIElementPropertyUndoUnit.cs
- SessionEndedEventArgs.cs
- ChtmlTextWriter.cs
- HostedHttpTransportManager.cs
- DeploymentSectionCache.cs
- DrawingGroup.cs
- FixedTextPointer.cs
- AlignmentXValidation.cs
- Util.cs
- XamlTypeMapperSchemaContext.cs
- DataBoundControl.cs