Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / System / Windows / Data / BindingExpressionBase.cs / 4 / BindingExpressionBase.cs
//---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: Defines BindingExpressionBase object, // base class for BindingExpression, PriorityBindingExpression, // and MultiBindingExpression. // // See spec at http://avalon/connecteddata/Specs/Data%20Binding.mht // //--------------------------------------------------------------------------- // build with this symbol defined to catch errors about not using // BindingExpression.GetReference correctly //#define USE_ITEM_REFERENCE using System; using System.Collections.ObjectModel; // Collectionusing System.ComponentModel; // TypeConverter using System.Diagnostics; // StackTrace using System.Globalization; // CultureInfo using System.Windows; // FrameworkElement using System.Windows.Controls; // Validation using System.Windows.Markup; // XmlLanguage using System.Windows.Threading; // Dispatcher using MS.Internal; // Invariant.Assert using MS.Internal.Controls; // ValidationErrorCollection using MS.Internal.Data; // DataBindEngine using MS.Internal.KnownBoxes; // BooleanBoxes using MS.Internal.Utility; // TraceLog namespace System.Windows.Data { /// /// Base class for Binding Expressions. /// public abstract class BindingExpressionBase : Expression, IWeakEventListener { // Flags indicating run-time properties of a BindingExpression [Flags] internal enum BindingFlags { // names used by Binding OneWay = PrivateFlags.iSourceToTarget, TwoWay = PrivateFlags.iSourceToTarget | PrivateFlags.iTargetToSource, OneWayToSource = PrivateFlags.iTargetToSource, OneTime = 0, PropDefault = PrivateFlags.iPropDefault, NotifyOnTargetUpdated = PrivateFlags.iNotifyOnTargetUpdated, NotifyOnSourceUpdated = PrivateFlags.iNotifyOnSourceUpdated, NotifyOnValidationError = PrivateFlags.iNotifyOnValidationError, UpdateOnPropertyChanged = 0, UpdateOnLostFocus = PrivateFlags.iUpdateOnLostFocus, UpdateExplicitly = PrivateFlags.iUpdateExplicitly, UpdateDefault = PrivateFlags.iUpdateDefault, PathGeneratedInternally = PrivateFlags.iPathGeneratedInternally, ValidatesOnExceptions = PrivateFlags.iValidatesOnExceptions, ValidatesOnDataErrors = PrivateFlags.iValidatesOnDataErrors, Default = PropDefault | UpdateDefault, ///Error value, returned by FlagsFrom to indicate faulty input IllegalInput = PrivateFlags.iIllegalInput, PropagationMask = OneWay | TwoWay | OneWayToSource | OneTime | PropDefault, UpdateMask = UpdateOnPropertyChanged | UpdateOnLostFocus | UpdateExplicitly | UpdateDefault, } [Flags] private enum PrivateFlags { // internal use iSourceToTarget = 0x00000001, iTargetToSource = 0x00000002, iPropDefault = 0x00000004, iNotifyOnTargetUpdated = 0x00000008, iDefaultValueConverter = 0x00000010, iInTransfer = 0x00000020, iInUpdate = 0x00000040, iTransferPending = 0x00000080, iNeedDataTransfer = 0x00000100, iTransferDeferred = 0x00000200, // used by MultiBindingExpression iUpdateOnLostFocus = 0x00000400, iUpdateExplicitly = 0x00000800, iUpdateDefault = iUpdateExplicitly | iUpdateOnLostFocus, iNeedUpdate = 0x00001000, iPathGeneratedInternally = 0x00002000, iUsingMentor = 0x00004000, iResolveNamesInTemplate = 0x00008000, iDetaching = 0x00010000, iNeedsCollectionView = 0x00020000, iInPriorityBindingExpression= 0x00040000, iInMultiBindingExpression = 0x00080000, iUsingFallbackValue = 0x00100000, iNotifyOnValidationError = 0x00200000, iAttaching = 0x00400000, iNotifyOnSourceUpdated = 0x00800000, iValidatesOnExceptions = 0x01000000, iValidatesOnDataErrors = 0x02000000, iIllegalInput = 0x04000000, iNeedsValidation = 0x08000000, iPropagationMask = iSourceToTarget | iTargetToSource | iPropDefault, iUpdateMask = iUpdateOnLostFocus | iUpdateExplicitly, } //----------------------------------------------------- // // Constructors // //----------------------------------------------------- ///Constructor internal BindingExpressionBase(BindingBase binding, BindingExpressionBase parent) : base(ExpressionMode.SupportsUnboundSources) { if (binding == null) throw new ArgumentNullException("binding"); _binding = binding; _parentBindingExpression = parent; _flags = (PrivateFlags)binding.Flags; if (parent != null) { ResolveNamesInTemplate = parent.ResolveNamesInTemplate; Type type = parent.GetType(); if (type == typeof(MultiBindingExpression)) ChangeFlag(PrivateFlags.iInMultiBindingExpression, true); else if (type == typeof(PriorityBindingExpression)) ChangeFlag(PrivateFlags.iInPriorityBindingExpression, true); } // initialize tracing information PresentationTraceLevel traceLevel = PresentationTraceSources.GetTraceLevel(binding); if (traceLevel > 0) { // copy TraceLevel from parent BindingBase - it can be changed later PresentationTraceSources.SetTraceLevel(this, traceLevel); } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.CreateExpression)) { if (parent == null) { TraceData.Trace(TraceEventType.Warning, TraceData.CreatedExpression( TraceData.Identify(this), TraceData.Identify(binding))); } else { TraceData.Trace(TraceEventType.Warning, TraceData.CreatedExpressionInParent( TraceData.Identify(this), TraceData.Identify(binding), TraceData.Identify(parent))); } } if (LookupValidationRule(typeof(ExceptionValidationRule)) != null) { ChangeFlag(PrivateFlags.iValidatesOnExceptions, true); } if (LookupValidationRule(typeof(DataErrorValidationRule)) != null) { ChangeFlag(PrivateFlags.iValidatesOnDataErrors, true); } } //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- ///Binding from which this expression was created public BindingBase ParentBindingBase { get { return _binding; } } ///Status of the BindingExpression public BindingStatus Status { get { return _status; } } ////// The ValidationError that caused this /// BindingExpression to be invalid. /// public virtual ValidationError ValidationError { get { return _validationError; } } ////// HasError returns true if any of the ValidationRules /// in the ParentBinding failed its validation rule. /// public virtual bool HasError { get { return _validationError != null; } } //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ ///Force a data transfer from source to target public virtual void UpdateTarget() { } ///Send the current value back to the source ///Does nothing when binding's Mode is not TwoWay or OneWayToSource public virtual void UpdateSource() { } #region Expression overrides ////// Notification that the Expression has been set as a property's value /// ////// Subclasses should not override OnAttach(), but must override Attach() /// /// DependencyObject being set /// Property being set internal sealed override void OnAttach(DependencyObject d, DependencyProperty dp) { if (d == null) throw new ArgumentNullException("d"); if (dp == null) throw new ArgumentNullException("dp"); Attach(d, dp); } ////// Notification that the Expression has been removed as a property's value /// ////// Subclasses should not override OnDetach(), but must override Detach() /// /// DependencyObject being cleared /// Property being cleared internal sealed override void OnDetach(DependencyObject d, DependencyProperty dp) { Detach(); } ////// List of sources of the Expression /// ///Sources list internal override DependencySource[] GetSources() { int j, k; int n = (_sources != null) ? _sources.Length : 0; if (n == 0) return null; DependencySource[] result = new DependencySource[n]; // convert the weak references into strong ones for (j=0, k=0; k/// Handle events from the centralized event table /// bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { return ReceiveWeakEvent(managerType, sender, e); } #region BindingExpressions with no target DP //----------------------------------------------------- // // API for BindingExpressions with no target DP (task 20769) // This is internal for now, until we review whether we wish to // make it public // //------------------------------------------------------ /// Create an untargeted BindingExpression internal static BindingExpressionBase CreateUntargetedBindingExpression(DependencyObject d, BindingBase binding) { return binding.CreateBindingExpression(d, NoTargetProperty); } ///Attach the BindingExpression to its target element ////// This method must be called once during the initialization. /// /// The target element internal void Attach(DependencyObject d) { Attach(d, NoTargetProperty); } ///This event is raised when the BindingExpression's value changes internal event EventHandlerValueChanged; /* The following APIs are also needed for untargeted bindings, but they already exist for other reasons. /// The current value of the BindingExpression internal object Value { get; set; } ///Activate the BindingExpression, using the given item as its root item. internal void Activate(object item) {} ///Deactivate the BindingExpression. internal void Deactivate() {} ///Detach the BindingExpression from its target element ////// This method must be called once when the BindingExpression is no longer needed. /// internal void Detach() {} */ #endregion BindingExpressions with no target DP //----------------------------------------------------- // // Protected Properties // //----------------------------------------------------- ///True if this binding expression is attaching internal bool IsAttaching { get { return TestFlag(PrivateFlags.iAttaching); } set { ChangeFlag(PrivateFlags.iAttaching, value); } } ///True if this binding expression is detaching internal bool IsDetaching { get { return TestFlag(PrivateFlags.iDetaching); } set { ChangeFlag(PrivateFlags.iDetaching, value); } } ///True if this binding expression updates the target internal bool IsDynamic { get { return ( TestFlag(PrivateFlags.iSourceToTarget) && (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsDynamic)); } } ///True if this binding expression updates the source internal bool IsReflective { get { return ( TestFlag(PrivateFlags.iTargetToSource) && (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsReflective)); } set { ChangeFlag(PrivateFlags.iTargetToSource, value); } } ///True if this binding expression uses a default ValueConverter internal bool UseDefaultValueConverter { get { return TestFlag(PrivateFlags.iDefaultValueConverter); } set { ChangeFlag(PrivateFlags.iDefaultValueConverter, value); } } ///True if this binding expression is OneWayToSource internal bool IsOneWayToSource { get { return (_flags & PrivateFlags.iPropagationMask) == PrivateFlags.iTargetToSource; } } ///True if this binding expression updates on PropertyChanged internal bool IsUpdateOnPropertyChanged { get { return (_flags & PrivateFlags.iUpdateMask) == 0; } } ///True if this binding expression updates on LostFocus internal bool IsUpdateOnLostFocus { get { return TestFlag(PrivateFlags.iUpdateOnLostFocus); } } ///True if this binding expression has a pending target update internal bool IsTransferPending { get { return TestFlag(PrivateFlags.iTransferPending); } set { ChangeFlag(PrivateFlags.iTransferPending, value); } } ///True if this binding expression is deferring a target update internal bool TransferIsDeferred { get { return TestFlag(PrivateFlags.iTransferDeferred); } set { ChangeFlag(PrivateFlags.iTransferDeferred, value); } } ///True if this binding expression is updating the target internal bool IsInTransfer { get { return TestFlag(PrivateFlags.iInTransfer); } set { ChangeFlag(PrivateFlags.iInTransfer, value); } } ///True if this binding expression is updating the source internal bool IsInUpdate { get { return TestFlag(PrivateFlags.iInUpdate); } set { ChangeFlag(PrivateFlags.iInUpdate, value); } } ///True if this binding expression is using the fallback value internal bool UsingFallbackValue { get { return TestFlag(PrivateFlags.iUsingFallbackValue); } set { ChangeFlag(PrivateFlags.iUsingFallbackValue, value); } } ///True if this binding expression uses the mentor of the target element internal bool UsingMentor { get { return TestFlag(PrivateFlags.iUsingMentor); } set { ChangeFlag(PrivateFlags.iUsingMentor, value); } } ///True if this binding expression should resolve ElementName within the template of the target element internal bool ResolveNamesInTemplate { get { return TestFlag(PrivateFlags.iResolveNamesInTemplate); } set { ChangeFlag(PrivateFlags.iResolveNamesInTemplate, value); } } ///True if this binding expression has a pending target update internal bool NeedsDataTransfer { get { return TestFlag(PrivateFlags.iNeedDataTransfer); } set { ChangeFlag(PrivateFlags.iNeedDataTransfer, value); } } ///True if this binding expression has a pending source update internal bool NeedsUpdate { get { return TestFlag(PrivateFlags.iNeedUpdate); } set { ChangeFlag(PrivateFlags.iNeedUpdate, value); if (value) { NeedsValidation = true; } } } ///True if this binding expression needs validation internal bool NeedsValidation { get { return TestFlag(PrivateFlags.iNeedsValidation); } set { ChangeFlag(PrivateFlags.iNeedsValidation, value); } } ///True if this binding expression should raise the TargetUpdated event internal bool NotifyOnTargetUpdated { get { return TestFlag(PrivateFlags.iNotifyOnTargetUpdated); } set { ChangeFlag(PrivateFlags.iNotifyOnTargetUpdated, value); } } ///True if this binding expression should raise the SourceUpdated event internal bool NotifyOnSourceUpdated { get { return TestFlag(PrivateFlags.iNotifyOnSourceUpdated); } set { ChangeFlag(PrivateFlags.iNotifyOnSourceUpdated, value); } } ///True if this binding expression should raise the ValidationError event internal bool NotifyOnValidationError { get { return TestFlag(PrivateFlags.iNotifyOnValidationError); } set { ChangeFlag(PrivateFlags.iNotifyOnValidationError, value); } } ///True if this binding expression belongs to a PriorityBinding internal bool IsInPriorityBindingExpression { get { return TestFlag(PrivateFlags.iInPriorityBindingExpression); } } ///True if this binding expression belongs to a MultiBinding internal bool IsInMultiBindingExpression { get { return TestFlag(PrivateFlags.iInMultiBindingExpression); } } ///True if this binding expression belongs to a PriorityBinding or MultiBinding internal bool IsInBindingExpressionCollection { get { return TestFlag(PrivateFlags.iInPriorityBindingExpression | PrivateFlags.iInMultiBindingExpression); } } ///True if this binding expression validates on exceptions internal bool ValidatesOnExceptions { get { return TestFlag(PrivateFlags.iValidatesOnExceptions); } } ///True if this binding expression validates on data errors internal bool ValidatesOnDataErrors { get { return TestFlag(PrivateFlags.iValidatesOnDataErrors); } } ///The parent MultiBindingExpression (if any) internal MultiBindingExpression ParentMultiBindingExpression { get { return _parentBindingExpression as MultiBindingExpression; } } ///The parent PriorityBindingExpression (if any) internal PriorityBindingExpression ParentPriorityBindingExpression { get { return _parentBindingExpression as PriorityBindingExpression; } } ///The parent PriorityBindingExpression or MultiBindingExpression (if any) internal BindingExpressionBase ParentBindingExpressionBase { get { return _parentBindingExpression; } } ///The FallbackValue (from the parent Binding), possibly converted /// to a type suitable for the target property. internal object FallbackValue { // perf note: we recompute the value every time it's needed. This is // a good decision if we seldom need the value. Alternatively we could // cache it. Wait until we know what the perf impact really is. get { return ConvertFallbackValue(ParentBindingBase.FallbackValue, TargetProperty, this); } } ///The default value of the target property internal object DefaultValue { // perf note: we recompute the value every time it's needed. This is // a good decision if we seldom need the value. Alternatively we could // cache it. Wait until we know what the perf impact really is. get { DependencyObject target = TargetElement; if (target != null) { return TargetProperty.GetDefaultValue(target.DependencyObjectType); } return DependencyProperty.UnsetValue; } } ///The effective string format, taking into account the target /// property, parent bindings, convenience syntax, etc. internal string EffectiveStringFormat { get { return _effectiveStringFormat; } } ///The effective TargetNullValue, taking into account the target /// property, parent bindings, etc. internal object EffectiveTargetNullValue { get { return _effectiveTargetNullValue; } } ///return the root of the tree of {Multi/Priority}BindingExpressions internal BindingExpressionBase RootBindingExpression { get { BindingExpressionBase child = this; BindingExpressionBase parent = this.ParentBindingExpressionBase; while (parent != null) { child = parent; parent = child.ParentBindingExpressionBase; } return child; } } ///return the binding group to which this expression belongs (or null) internal BindingGroup BindingGroup { get { BindingExpressionBase root = RootBindingExpression; WeakReference wr = root._bindingGroup; return (wr == null) ? null : (BindingGroup)wr.Target; } } internal virtual bool IsParentBindingUpdateTriggerDefault { get { return false; } } //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ ////// Attach the binding expression to the given target object and property. /// Derived classes should call base.AttachOverride before doing their work. /// internal virtual void AttachOverride(DependencyObject target, DependencyProperty dp) { _targetElement = new WeakReference(target); _targetProperty = dp; // get the engine _engine = DataBindEngine.CurrentDataBindEngine; DetermineEffectiveStringFormat(); DetermineEffectiveTargetNullValue(); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach)) { TraceData.Trace(TraceEventType.Warning, TraceData.AttachExpression( TraceData.Identify(this), target.GetType().FullName, dp.Name, AvTrace.GetHashCodeHelper(target))); } } ////// Detach the binding expression from its target object and property. /// Derived classes should call base.DetachOverride after doing their work. /// internal virtual void DetachOverride() { _engine = null; _targetElement = null; _targetProperty = null; SetStatus(BindingStatus.Detached); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach)) { TraceData.Trace(TraceEventType.Warning, TraceData.DetachExpression( TraceData.Identify(this))); } } ////// Invalidate the given child expression. /// internal abstract void InvalidateChild(BindingExpressionBase bindingExpression); ////// Change the dependency sources for the given child expression. /// internal abstract void ChangeSourcesForChild(BindingExpressionBase bindingExpression, WeakDependencySource[] newSources); ////// Replace the given child expression with a new one. /// internal abstract void ReplaceChild(BindingExpressionBase bindingExpression); // handle joining or leaving a binding group internal void OnBindingGroupChanged(bool joining) { if (joining) { // joining a binding group: // update Explicitly, unless declared otherwise if (IsParentBindingUpdateTriggerDefault) { if (IsUpdateOnLostFocus) { LostFocusEventManager.RemoveListener(TargetElement, this); } SetUpdateSourceTrigger(UpdateSourceTrigger.Explicit); } } else { // leaving a binding group: // restore update trigger if (IsParentBindingUpdateTriggerDefault) { FrameworkPropertyMetadata fwMetaData = TargetProperty.GetMetadata(TargetElement.DependencyObjectType) as FrameworkPropertyMetadata; UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData); SetUpdateSourceTrigger(ust); if (IsUpdateOnLostFocus) { LostFocusEventManager.AddListener(TargetElement, this); } } } } // register the leaf bindings with the binding group internal abstract void UpdateBindingGroup(BindingGroup bg); // transfer a value from target to source internal void UpdateValue() { UpdateValidationError(null); object value = GetRawProposedValue(); if (!Validate(value, ValidationStep.RawProposedValue)) return; value = ConvertProposedValue(value); if (!Validate(value, ValidationStep.ConvertedProposedValue)) return; value = UpdateSource(value); if (!Validate(value, ValidationStep.UpdatedValue)) return; value = CommitSource(value); if (!Validate(value, ValidationStep.CommittedValue)) return; EndSourceUpdate(); } ////// Get the raw proposed value /// internal virtual object GetRawProposedValue() { object value = Value; // TargetNullValue is the UI representation of a "null" value. Use null internally. if (Object.Equals(value, EffectiveTargetNullValue)) { value = null; } return value; } /// /// Get the converted proposed value /// internal abstract object ConvertProposedValue(object rawValue); /// /// Get the converted proposed value and inform the binding group /// internal abstract void ObtainConvertedProposedValue(BindingGroup bindingGroup); /// /// Update the source value /// internal abstract object UpdateSource(object convertedValue); /// /// Update the source value and inform the binding group /// internal abstract void UpdateSource(BindingGroup bindingGroup); /// /// Commit the source value /// internal virtual object CommitSource(object value) { return value; } /// /// Store the value in the binding group /// internal abstract void StoreValueInBindingGroup(object value, BindingGroup bindingGroup); ////// Run validation rules for the given step /// internal virtual bool Validate(object value, ValidationStep validationStep) { if (value == Binding.DoNothing || value == DependencyProperty.UnsetValue) return true; // clear errors from this step - we're about to reevaluate those rules ClearValidationErrors(validationStep); Collection validationRules = ParentBindingBase.ValidationRulesInternal; if (validationRules != null) { CultureInfo culture = GetCulture(); switch (validationStep) { case ValidationStep.UpdatedValue: case ValidationStep.CommittedValue: // rules at these steps get passed the rule owner value = this; break; } foreach (ValidationRule validationRule in validationRules) { if (validationRule.ValidationStep == validationStep) { ValidationResult validationResult = validationRule.Validate(value, culture); if (!validationResult.IsValid) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update)) { TraceData.Trace(TraceEventType.Warning, TraceData.ValidationRuleFailed( TraceData.Identify(this), TraceData.Identify(validationRule))); } UpdateValidationError( new ValidationError(validationRule, this, validationResult.ErrorContent, null)); return false; // kenlai: } } } } return true; } /// /// Run validation rules for the given step, and inform the binding group /// internal abstract bool CheckValidationRules(BindingGroup bindingGroup, ValidationStep validationStep); /// bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { return ReceiveWeakEvent(managerType, sender, e); } #region BindingExpressions with no target DP //----------------------------------------------------- // // API for BindingExpressions with no target DP (task 20769) // This is internal for now, until we review whether we wish to // make it public // //------------------------------------------------------ ////// Compute the culture, either from the parent Binding, or from the target element. /// internal CultureInfo GetCulture() { // lazy initialization, to let the target element acquire all its properties if (_culture == DefaultValueObject) { // explicit culture set in Binding _culture = ParentBindingBase.ConverterCultureInternal; // if that doesn't work, use target element's xml:lang property if (_culture == null) { DependencyObject target = TargetElement; if (target != null) { if (IsInTransfer && (TargetProperty == FrameworkElement.LanguageProperty)) { // A binding for the Language property needs the value // of the Language property. This circularity is not // supported (bug 1274874). if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Critical, TraceData.RequiresExplicitCulture, TargetProperty.Name, this); } throw new InvalidOperationException(SR.Get(SRID.RequiresExplicitCulture, TargetProperty.Name)); } // cache CultureInfo since requerying an inheritable property on every Transfer/Update can be quite expensive // CultureInfo DP rarely changes once a XAML document is loaded. // To be 100% correct, changes to the CultureInfo attached DP should be tracked // and cause a re-evaluation of this binding. _culture = ((XmlLanguage) target.GetValue(FrameworkElement.LanguageProperty)).GetSpecificCulture(); } } } return (CultureInfo)_culture; } ///Begin a source update internal void BeginSourceUpdate() { ChangeFlag(PrivateFlags.iInUpdate, true); } ///End a source update internal void EndSourceUpdate() { ChangeFlag(PrivateFlags.iInUpdate | PrivateFlags.iNeedUpdate, false); } ///change the value to the new value, and notify listeners internal void ChangeValue(object newValue, bool notify) { object oldValue = (_value != DefaultValueObject) ? _value : DependencyProperty.UnsetValue; _value = newValue; if (notify && ValueChanged != null) { ValueChanged(this, new BindingValueChangedEventArgs(oldValue, newValue)); } } ///the target value has changed - the source needs to be updated internal void Dirty() { if (!IsInTransfer) { NeedsUpdate = true; if (IsUpdateOnPropertyChanged) Update(true); } } ///use the Fallback or Default value, called when a real value is not available internal object UseFallbackValue() { object value = FallbackValue; // if there's a problem with the fallback, use Default instead if (value == DefaultValueObject) { value = DependencyProperty.UnsetValue; } if (value != DependencyProperty.UnsetValue) { UsingFallbackValue = true; } else { // if fallback isn't available, use Default (except when in a binding collection) if (Status == BindingStatus.Active) SetStatus(BindingStatus.UpdateTargetError); if (!IsInBindingExpressionCollection) { value = DefaultValue; if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Information, TraceData.NoValueToTransfer, this); } } } return value; } // determine if the given value is "null" (in a general sense) internal bool IsNullValue(object value) { if (value == null) return true; if (Convert.IsDBNull(value)) return true; if (IsSqlNull(value)) return true; return false; } // return true if the value is null in the SqlTypes sense bool IsSqlNull(object value) { Type type = value.GetType(); return Engine.IsINullable(type) && IsSqlNullImpl(value); } // separate function to avoid loading System.Data unnecessarily [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] static bool IsSqlNullImpl(object value) { System.Data.SqlTypes.INullable nullable = value as System.Data.SqlTypes.INullable; return (nullable != null && nullable.IsNull); } // determine a "null" value appropriate for the given type internal object NullValueForType(Type type) { if (type == null) return null; object sqlNull; if (IsSqlNullableType(type, out sqlNull)) return sqlNull; if (!type.IsValueType) return null; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return null; return DependencyProperty.UnsetValue; } // return true if the type is nullable in the SqlTypes sense. If so, return the null value. bool IsSqlNullableType(Type type, out object nullValue) { if (Engine.IsINullable(type)) { return IsSqlNullableTypeImpl(type, out nullValue); } else { nullValue = null; return false; } } // separate function to avoid loading System.Data unnecessarily [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] static bool IsSqlNullableTypeImpl(Type type, out object nullValue) { // some SqlTypes are classes with a Null property. Others are structs with a Null field. Try both. System.Reflection.FieldInfo nullField = type.GetField("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy); if (nullField != null) { nullValue = nullField.GetValue(null); return true; } System.Reflection.PropertyInfo nullProperty = type.GetProperty("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy); if (nullProperty != null) { nullValue = nullProperty.GetValue(null, null); return true; } nullValue = null; return false; } internal ValidationRule LookupValidationRule(Type type) { ValidationRule result = ParentBindingBase.GetValidationRule(type); if (result == null && _parentBindingExpression != null) { result = _parentBindingExpression.LookupValidationRule(type); } return result; } // discover the binding group (if any) that this binding should join, // and join it. More precisely, cause the root binding to join. internal void JoinBindingGroup(bool isReflective, DependencyObject contextElement) { BindingGroup bindingGroup = RootBindingExpression.FindBindingGroup(isReflective, contextElement); if (bindingGroup != null) { JoinBindingGroup(bindingGroup, /*explicit*/false); } } // remove the binding from its group internal void LeaveBindingGroup() { BindingExpressionBase root = RootBindingExpression; BindingGroup bg = root.BindingGroup; if (bg != null) { bg.BindingExpressions.Remove(root); root._bindingGroup = null; } } // discover the binding group (if any) that this root binding should join. BindingGroup FindBindingGroup(bool isReflective, DependencyObject contextElement) { Debug.Assert(RootBindingExpression == this, "Only call this with a root binding"); // if we've already joined (or failed to join) a group, just return the result if (_bindingGroup != null) { return (BindingGroup)_bindingGroup.Target; } BindingGroup bg; string groupName = ParentBindingBase.BindingGroupName; // a null group name means "don't join any group". if (groupName == null) { MarkAsNonGrouped(); return null; } // an empty group name means join by DataContext if (String.IsNullOrEmpty(groupName)) { // check further preconditions: if (!isReflective || // must have target-to-source data flow contextElement == null) // must use data context { // later child bindings might pass this test, so don't mark // this root binding as non-grouped yet return null; } // only the innermost binding group is eligible bg = (BindingGroup)contextElement.GetValue(FrameworkElement.BindingGroupProperty); if (bg == null) { MarkAsNonGrouped(); return null; } // the context element must share data context with the group DependencyProperty dataContextDP = FrameworkElement.DataContextProperty; DependencyObject groupContextElement = bg.InheritanceContext; if (groupContextElement == null || !Object.Equals( contextElement.GetValue(dataContextDP), groupContextElement.GetValue(dataContextDP))) { MarkAsNonGrouped(); return null; } // if the binding survives the gauntlet, return the group return bg; } // a non-empty group name means join by name else { // walk up the tree, looking for a matching binding group DependencyProperty bindingGroupDP = FrameworkElement.BindingGroupProperty; FrameworkObject fo = new FrameworkObject(TargetElement); while (fo.DO != null) { BindingGroup bgCandidate = (BindingGroup)fo.DO.GetValue(bindingGroupDP); if (bgCandidate == null) { MarkAsNonGrouped(); return null; } if (bgCandidate.Name == groupName) { // return the matching group return bgCandidate; } fo = fo.FrameworkParent; } // no match - report an error if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.BindingGroupNameMatchFailed(groupName), this); } MarkAsNonGrouped(); return null; } } // add a binding to the given binding group internal void JoinBindingGroup(BindingGroup bg, bool explicitJoin) { BindingExpressionBase root = null; // set to non-null by the next loop for ( BindingExpressionBase bindingExpr = this; bindingExpr != null; bindingExpr = bindingExpr.ParentBindingExpressionBase) { root = bindingExpr; // bindings in a group update Explicitly, unless declared otherwise bindingExpr.OnBindingGroupChanged(/*joining*/true); bg.AddToValueTable(bindingExpr); } // add the root binding to the group if (root._bindingGroup == null) { // use WeakReference because the BindingGroup contains a strong reference // to the visual tree (via InheritanceContext) root._bindingGroup = new WeakReference(bg); // when the group is implicitly discovered, always add the root binding to the group's collection. // When the binding is added explicitly - via BindingGroup.BindingExpressions.Add() - // check first to see if it has already been added bool addToGroup = explicitJoin ? !bg.BindingExpressions.Contains(root) : true; if (addToGroup) { bg.BindingExpressions.Add(root); } // in the explicit case, register its items and values with the binding group // (the implicit case does this when the bindings activate) if (explicitJoin) { root.UpdateBindingGroup(bg); } } else { if (root.BindingGroup != bg) throw new InvalidOperationException(SR.Get(SRID.BindingGroup_CannotChangeGroups)); } } // mark a binding as non-grouped, so that we avoid doing the discovery again void MarkAsNonGrouped() { // Leaf bindings only get asked once, so there's no need to add a mark if (!(this is BindingExpression)) { _bindingGroup = new WeakReference(null); } } ////// Handle events from the centralized event table /// internal virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { return false; // unrecognized event } private bool TestFlag(PrivateFlags flag) { return (_flags & flag) != 0; } private void ChangeFlag(PrivateFlags flag, bool value) { if (value) _flags |= flag; else _flags &= ~flag; } //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ internal DependencyProperty TargetProperty { get { return _targetProperty; } } // A BindingExpression cannot hold a strong reference to the target element - this // leads to memory leaks (bug 871139). The problem is that BindingExpression and its workers // register for events from the data item, creating a reference from // the data item to the BindingExpression. The data item typically has a long lifetime, // so if the BindingExpression held a SR to the target, the target element would // also stay alive forever. // Instead, BindingExpression holds a WeakReference to the target. This means we // have to check it before dereferencing (here), and cope when the // reference fails (in callers to this property). Requests for the TargetElement // are not trivial, so callers should request it once and cache the result // in a local variable. They should not save it in a global or instance // variable of course; that would defeat the purpose of the WR. // This allows the target element to be GC'd when it's no longer in // use by the tree or application. The next time the BindingExpression asks for // its TargetElement, the WR will fail. At this point, the BindingExpression is no // longer useful, so it can sever all connections to the outside world (i.e. // stop listening for events). This allows the BindingExpression itself to be GC'd. internal DependencyObject TargetElement { get { if (_targetElement != null) { DependencyObject result = _targetElement.Target as DependencyObject; if (result != null) return result; // target has been GC'd, sever all connections _targetElement = null; // prevents re-entry from Detach() Detach(); } return null; } } internal WeakReference TargetElementReference { get { return _targetElement; } } internal DataBindEngine Engine { get { return _engine; } } internal Dispatcher Dispatcher { get { return (_engine != null) ? _engine.Dispatcher : null; } } internal object Value { get { if (_value == DefaultValueObject) { // don't notify listeners. This isn't a real value change. ChangeValue(UseFallbackValue(), false /*notify*/); } return _value; } set { ChangeValue(value, true); Dirty(); } } internal WeakDependencySource[] WeakSources { get { return _sources; } } ////// NoTarget DependencyProperty, a placeholder used by BindingExpressions with no target property /// internal static readonly DependencyProperty NoTargetProperty = DependencyProperty.RegisterAttached("NoTarget", typeof(object), typeof(BindingExpressionBase), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None)); internal TraceLog TraceLog { get { return _traceLog; } } //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- ////// Attach the binding expression to the given target object and property. /// internal void Attach(DependencyObject target, DependencyProperty dp) { // make sure we're on the right thread to access the target if (target != null) { target.VerifyAccess(); } IsAttaching = true; AttachOverride(target, dp); IsAttaching = false; } ////// Detach the binding expression from its target object and property. /// internal void Detach() { if (_status == BindingStatus.Detached || IsDetaching) return; IsDetaching = true; DetachOverride(); IsDetaching = false; } internal void SetStatus(BindingStatus status) { if (_status == BindingStatus.Detached && status != _status) { throw new InvalidOperationException(SR.Get(SRID.BindingExpressionStatusChanged, _status, status)); } _status = status; if (_traceLog != null) { _traceLog.Add("Set status = {0} {1}", _status, new StackTrace()); } } // convert a user-supplied fallback value to a usable equivalent // returns: UnsetValue if user did not supply a fallback value // value if fallback value is legal // DefaultValueObject otherwise internal static object ConvertFallbackValue(object value, DependencyProperty dp, object sender) { Exception e; object result = ConvertValue(value, dp, out e); if (result == DefaultValueObject) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.FallbackConversionFailed( AvTrace.ToStringHelper(value), AvTrace.TypeName(value), dp.Name, dp.PropertyType.Name), sender, e); } } return result; } // convert a user-supplied TargetNullValue to a usable equivalent // returns: UnsetValue if user did not supply a fallback value // value if fallback value is legal // DefaultValueObject otherwise internal static object ConvertTargetNullValue(object value, DependencyProperty dp, object sender) { Exception e; object result = ConvertValue(value, dp, out e); if (result == DefaultValueObject) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.TargetNullValueConversionFailed( AvTrace.ToStringHelper(value), AvTrace.TypeName(value), dp.Name, dp.PropertyType.Name), sender, e); } } return result; } static object ConvertValue(object value, DependencyProperty dp, out Exception e) { object result; e = null; if (value == DependencyProperty.UnsetValue || dp.IsValidValue(value)) { result = value; } else { result = null; // placeholder to keep compiler happy // if value isn't the right type, use a type converter to get a better value bool success = false; TypeConverter converter = DefaultValueConverter.GetConverter(dp.PropertyType); if (converter != null && converter.CanConvertFrom(value.GetType())) { // PreSharp uses message numbers that the C# compiler doesn't know about. // Disable the C# complaints, per the PreSharp documentation. #pragma warning disable 1634, 1691 // PreSharp complains about catching NullReference (and other) exceptions. // It doesn't recognize that IsCriticalException() handles these correctly. #pragma warning disable 56500 try { result = converter.ConvertFrom(null, CultureInfo.InvariantCulture, value); success = dp.IsValidValue(result); } // Catch all exceptions. If we can't convert the fallback value, it doesn't // matter why not; we should always use the default value instead. // (See bug 1853628 for an example of a converter that throws // an exception not mentioned in the documentation for ConvertFrom.) catch (Exception ex) { e = ex; } catch // non CLS compliant exception { } #pragma warning restore 56500 #pragma warning restore 1634, 1691 } if (!success) { // if can't convert it, don't use it result = DefaultValueObject; } } return result; } // Certain trace reports should be marked as 'error' unless the binding // is prepared to handle it in some way (e.g. FallbackValue), in which // case 'warning'. internal TraceEventType TraceLevel { get { // FallbackValue is present if (ParentBindingBase.FallbackValue != DependencyProperty.UnsetValue) return TraceEventType.Warning; // Binding is a member of MultiBinding or PriorityBinding if (IsInBindingExpressionCollection) return TraceEventType.Warning; // all other cases - error return TraceEventType.Error; } } internal virtual void Activate() { } internal virtual void Deactivate() { } internal virtual void Update(bool synchronous) { } internal void UpdateValidationError(ValidationError validationError) { if (_validationError != null) { ValidationError oldValidationError = _validationError; _validationError = null; // clear before raising events, so that HasError is correct RemoveValidationError(oldValidationError); } _validationError = validationError; if (_validationError != null) { AddValidationError(_validationError); } } internal void AddValidationError(ValidationError validationError) { // add the error to the target element Validation.AddValidationError(validationError, TargetElement, NotifyOnValidationError); // add the error to the binding group's target element BindingGroup bindingGroup = BindingGroup; if (bindingGroup != null) { bindingGroup.AddValidationError(validationError); } } internal void RemoveValidationError(ValidationError validationError) { // remove the error from the target element Validation.RemoveValidationError(validationError, TargetElement, NotifyOnValidationError); // remove the error from the binding group's target element BindingGroup bindingGroup = BindingGroup; if (bindingGroup != null) { bindingGroup.RemoveValidationError(validationError); } } // remove all errors raised at the given step, in preparation for running // the rules at that step internal void ClearValidationErrors(ValidationStep validationStep) { if (_validationError == null || _validationError.RuleInError.ValidationStep != validationStep) return; RemoveValidationError(_validationError); _validationError = null; } internal void ChangeSources(WeakDependencySource[] newSources) { if (IsInBindingExpressionCollection) ParentBindingExpressionBase.ChangeSourcesForChild(this, newSources); else ChangeSources(TargetElement, TargetProperty, newSources); // store the sources with weak refs, so they don't cause memory leaks (bug 980041) _sources = newSources; } ////// combine the sources of BindingExpressions, using new sources for /// the BindingExpression at the given index /// /// -1 to indicate no new sources /// collection of child binding expressions /// how many child expressions to include /// use null when no new sources ///internal static WeakDependencySource[] CombineSources(int index, Collection bindingExpressions, int count, WeakDependencySource[] newSources) { if (index == count) { // Be sure to include newSources if they are being appended count++; } Collection tempList = new Collection (); for (int i = 0; i < count; ++i) { BindingExpressionBase bindExpr = bindingExpressions[i]; WeakDependencySource[] sources = (i==index) ? newSources : (bindExpr != null) ? bindExpr.WeakSources : null; int m = (sources == null) ? 0 : sources.Length; for (int j = 0; j < m; ++j) { WeakDependencySource candidate = sources[j]; // don't add duplicate source for (int k = 0; k < tempList.Count; ++k) { WeakDependencySource prior = tempList[k]; if (candidate.DependencyObject == prior.DependencyObject && candidate.DependencyProperty == prior.DependencyProperty) { candidate = null; break; } } if (candidate != null) tempList.Add(candidate); } } WeakDependencySource[] result; if (tempList.Count > 0) { result = new WeakDependencySource[tempList.Count]; tempList.CopyTo(result, 0); tempList.Clear(); } else { result = null; } return result; } internal void ResolvePropertyDefaultSettings(BindingMode mode, UpdateSourceTrigger updateTrigger, FrameworkPropertyMetadata fwMetaData) { // resolve "property-default" dataflow if (mode == BindingMode.Default) { BindingFlags f = BindingFlags.OneWay; if (fwMetaData != null && fwMetaData.BindsTwoWayByDefault) { f = BindingFlags.TwoWay; } ChangeFlag(PrivateFlags.iPropagationMask, false); ChangeFlag((PrivateFlags)f, true); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults)) { TraceData.Trace(TraceEventType.Warning, TraceData.ResolveDefaultMode( TraceData.Identify(this), (f == BindingFlags.OneWay) ? BindingMode.OneWay : BindingMode.TwoWay)); } } Debug.Assert((_flags & PrivateFlags.iPropagationMask) != PrivateFlags.iPropDefault, "BindingExpression should not have Default propagation"); // resolve "property-default" update trigger if (updateTrigger == UpdateSourceTrigger.Default) { UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData); SetUpdateSourceTrigger(ust); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults)) { TraceData.Trace(TraceEventType.Warning, TraceData.ResolveDefaultUpdate( TraceData.Identify(this), ust)); } } Invariant.Assert((_flags & PrivateFlags.iUpdateMask) != PrivateFlags.iUpdateDefault, "BindingExpression should not have Default update trigger"); } // return the effective update trigger, used when binding doesn't set one explicitly internal UpdateSourceTrigger GetDefaultUpdateSourceTrigger(FrameworkPropertyMetadata fwMetaData) { UpdateSourceTrigger ust = IsInMultiBindingExpression ? UpdateSourceTrigger.Explicit : (fwMetaData != null) ? fwMetaData.DefaultUpdateSourceTrigger : UpdateSourceTrigger.PropertyChanged; return ust; } internal void SetUpdateSourceTrigger(UpdateSourceTrigger ust) { ChangeFlag(PrivateFlags.iUpdateMask, false); ChangeFlag((PrivateFlags)BindingBase.FlagsFrom(ust), true); } internal void DetermineEffectiveStringFormat() { Type targetType = TargetProperty.PropertyType; if (targetType != typeof(String)) { // if the target type isn't String, we don't need a string format return; // _effectiveStringFormat is already null } // determine the effective target type and the declared string format // by looking up the tree of binding expressions string stringFormat = ParentBindingBase.StringFormat; BindingExpressionBase be = this.ParentBindingExpressionBase; while (be != null) { if (be is MultiBindingExpression) { // MultiBindings should receive object values, not string targetType = typeof(Object); break; } else if (stringFormat == null && be is PriorityBindingExpression) { // use a PriorityBinding's string format, unless we already // have a more specific one stringFormat = be.ParentBindingBase.StringFormat; } be = be.ParentBindingExpressionBase; } // if we need a string format, cache it if (targetType == typeof(String)) { #if NotToday // special case: when these conditions all apply // a) target element belongs to a DataTemplate // b) template was found by implicit (type-based) lookup // c) container (ContentPresenter) has a string format // d) binding has no effective string format // then use the CP's string format. This enables scenarios like // // // // where you'd like to control the format at the point of use, // rather than at the point of template declaration. This is // especially useful when the template is declared in a global place // like app resources or a theme file. In particular it makes the // GroupStyle.HeaderStringFormat property work; the template for // GroupItem is defined in the theme, but needs to pick up formatting // from the app's markup. if (stringFormat == null) // (d) { FrameworkObject fo = new FrameworkObject(TargetElement); ContentPresenter cp = fo.TemplatedParent as ContentPresenter; if (cp != null && // (a) cp.ContentStringFormat != null) // (c) { DataTemplate dt = cp.TemplateInternal as DataTemplate; if (dt != null && // (a) dt.DataType != null) // (b) { stringFormat = cp.ContentStringFormat; } } } #endif if (!String.IsNullOrEmpty(stringFormat)) { _effectiveStringFormat = Helper.GetEffectiveStringFormat(stringFormat); } } } internal void DetermineEffectiveTargetNullValue() { Type targetType = TargetProperty.PropertyType; // determine the effective target type and the declared TargetNullValue // by looking up the tree of binding expressions object targetNullValue = ParentBindingBase.TargetNullValue; BindingExpressionBase be = this.ParentBindingExpressionBase; while (be != null) { if (be is MultiBindingExpression) { // MultiBindings should receive object values targetType = typeof(Object); break; } else if (targetNullValue == DependencyProperty.UnsetValue && be is PriorityBindingExpression) { // use a PriorityBinding's TargetNullValue, unless we already // have a more specific one targetNullValue = be.ParentBindingBase.TargetNullValue; } be = be.ParentBindingExpressionBase; } // if user declared a TargetNullValue, make sure it has the right type. if (targetNullValue != DependencyProperty.UnsetValue) { targetNullValue = ConvertTargetNullValue(targetNullValue, TargetProperty, this); if (targetNullValue == DefaultValueObject) { // if not, ignore it (having logged a trace message) targetNullValue = DependencyProperty.UnsetValue; } } // for back-compat, don't turn on TargetNullValue unless user explicitly // asks for it. This means users have to add TargetNullValue to get // pretty basic scenarios to work (e.g. binding a TextBox to a database // string field that supports DBNull). It's painful, but can't be // helped. #if TargetNullValueBC //BreakingChange // if no declared (or poorly declared) value, make one up if (targetNullValue == DependencyProperty.UnsetValue) { targetNullValue = NullValueForType(targetType); } #endif _effectiveTargetNullValue = targetNullValue; } // To prevent memory leaks, we store WeakReferences to certain objects // in various places: _dataItem, _sources, worker fields. The logic // for this is centralized in these two static methods. (See bug 940041) internal static object CreateReference(object item) { // One source of leaks is the reference cycle formed when a BindingExpression's // source item contains a reference chain to the target element: // target -> BindingExpression -> source item -> target // // Making the second link into a WeakReference incurs some cost, // so it should be avoided if we know the third link never exists. // We definitely can't avoid this when the item is a DependencyObject, // since it's reasonably common for the third link to occur (e.g. // a ContextMenu contains a link to its Popup, which has a property // bound back to the ContextMenu). // // For safety, we choose to use WeakRef all the time, unless the item is null. // Exception (bug 1124954): Keep a strong reference to // BindingListCollectionView - this keeps the underlying DataView // alive, when nobody else will. // Exception (bug 1970505): Don't allocate a WeakRef for the common // case of the NullDataItem if (item != null && !(item is BindingListCollectionView) && !(item == BindingExpression.NullDataItem)) { item = new WeakReference(item); } #if USE_ITEM_REFERENCE item = new ItemReference(item); #endif return item; } // like CreateReference, but use an existing WeakReference internal static object CreateReference(WeakReference item) { object result = item; #if USE_ITEM_REFERENCE result = new ItemReference(item); #endif return result; } // like CreateReference, except re-target the old WeakReference (if any) internal static object ReplaceReference(object oldReference, object item) { if (item != null && !(item is BindingListCollectionView) && !(item == BindingExpression.NullDataItem)) { #if USE_ITEM_REFERENCE // if this cast fails, it's because you have done a direct assignment of an // item to some field instead of assigning the result of CreateReference. oldReference = ((ItemReference)oldReference).Item; #endif WeakReference wr = oldReference as WeakReference; if (wr != null) { wr.Target = item; item = wr; } else { item = new WeakReference(item); } } #if USE_ITEM_REFERENCE item = new ItemReference(item); #endif return item; } internal static object GetReference(object reference) { if (reference == null) return null; #if USE_ITEM_REFERENCE // if this cast fails, it's because you have done a direct assignment of an // item to some field instead of assigning the result of CreateReference. reference = ((ItemReference)reference).Item; #endif WeakReference wr = reference as WeakReference; if (wr != null) return wr.Target; else return reference; } internal static void InitializeTracing(BindingExpressionBase expr, DependencyObject d, DependencyProperty dp) { BindingBase parent = expr.ParentBindingBase; } //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #if USE_ITEM_REFERENCE private class ItemReference { internal ItemReference(object item) { _item = item; } internal object Item { get { return _item; } } object _item; } #endif // change WeakDependencySources to (strong) DependencySources, and notify // the property engine about the new sources void ChangeSources(DependencyObject target, DependencyProperty dp, WeakDependencySource[] newSources) { DependencySource[] sources; if (newSources != null) { // convert weak reference to strong sources = new DependencySource[newSources.Length]; int n = 0; for (int i = 0; i < newSources.Length; ++i) { DependencyObject sourceDO = newSources[i].DependencyObject; if (sourceDO != null) { // only include sources that are still alive sources[n++] = new DependencySource(sourceDO, newSources[i].DependencyProperty); } } // if any of the sources were no longer alive, trim the array if (n < newSources.Length) { DependencySource[] temp; if (n > 0) { temp = new DependencySource[n]; Array.Copy(sources, 0, temp, 0, n); } else { temp = null; } sources = temp; } } else { sources = null; } // notify property engine ChangeSources(target, dp, sources); } // this method is here just to avoid the compiler error // error CS0649: Warning as Error: Field 'System.Windows.Data.BindingExpression._traceLog' is never assigned to, and will always have its default value null void InitializeTraceLog() { _traceLog = new TraceLog(20); } //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- BindingBase _binding; WeakReference _targetElement; DependencyProperty _targetProperty; DataBindEngine _engine; PrivateFlags _flags; BindingExpressionBase _parentBindingExpression; object _value = DefaultValueObject; BindingStatus _status; TraceLog _traceLog; ValidationError _validationError; WeakDependencySource[] _sources; string _effectiveStringFormat; object _effectiveTargetNullValue; WeakReference _bindingGroup; object _culture = DefaultValueObject; /// Sentinel meaning "field has its default value" internal static readonly object DefaultValueObject = new object(); } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: Defines BindingExpressionBase object, // base class for BindingExpression, PriorityBindingExpression, // and MultiBindingExpression. // // See spec at http://avalon/connecteddata/Specs/Data%20Binding.mht // //--------------------------------------------------------------------------- // build with this symbol defined to catch errors about not using // BindingExpression.GetReference correctly //#define USE_ITEM_REFERENCE using System; using System.Collections.ObjectModel; // Collectionusing System.ComponentModel; // TypeConverter using System.Diagnostics; // StackTrace using System.Globalization; // CultureInfo using System.Windows; // FrameworkElement using System.Windows.Controls; // Validation using System.Windows.Markup; // XmlLanguage using System.Windows.Threading; // Dispatcher using MS.Internal; // Invariant.Assert using MS.Internal.Controls; // ValidationErrorCollection using MS.Internal.Data; // DataBindEngine using MS.Internal.KnownBoxes; // BooleanBoxes using MS.Internal.Utility; // TraceLog namespace System.Windows.Data { /// /// Base class for Binding Expressions. /// public abstract class BindingExpressionBase : Expression, IWeakEventListener { // Flags indicating run-time properties of a BindingExpression [Flags] internal enum BindingFlags { // names used by Binding OneWay = PrivateFlags.iSourceToTarget, TwoWay = PrivateFlags.iSourceToTarget | PrivateFlags.iTargetToSource, OneWayToSource = PrivateFlags.iTargetToSource, OneTime = 0, PropDefault = PrivateFlags.iPropDefault, NotifyOnTargetUpdated = PrivateFlags.iNotifyOnTargetUpdated, NotifyOnSourceUpdated = PrivateFlags.iNotifyOnSourceUpdated, NotifyOnValidationError = PrivateFlags.iNotifyOnValidationError, UpdateOnPropertyChanged = 0, UpdateOnLostFocus = PrivateFlags.iUpdateOnLostFocus, UpdateExplicitly = PrivateFlags.iUpdateExplicitly, UpdateDefault = PrivateFlags.iUpdateDefault, PathGeneratedInternally = PrivateFlags.iPathGeneratedInternally, ValidatesOnExceptions = PrivateFlags.iValidatesOnExceptions, ValidatesOnDataErrors = PrivateFlags.iValidatesOnDataErrors, Default = PropDefault | UpdateDefault, ///Error value, returned by FlagsFrom to indicate faulty input IllegalInput = PrivateFlags.iIllegalInput, PropagationMask = OneWay | TwoWay | OneWayToSource | OneTime | PropDefault, UpdateMask = UpdateOnPropertyChanged | UpdateOnLostFocus | UpdateExplicitly | UpdateDefault, } [Flags] private enum PrivateFlags { // internal use iSourceToTarget = 0x00000001, iTargetToSource = 0x00000002, iPropDefault = 0x00000004, iNotifyOnTargetUpdated = 0x00000008, iDefaultValueConverter = 0x00000010, iInTransfer = 0x00000020, iInUpdate = 0x00000040, iTransferPending = 0x00000080, iNeedDataTransfer = 0x00000100, iTransferDeferred = 0x00000200, // used by MultiBindingExpression iUpdateOnLostFocus = 0x00000400, iUpdateExplicitly = 0x00000800, iUpdateDefault = iUpdateExplicitly | iUpdateOnLostFocus, iNeedUpdate = 0x00001000, iPathGeneratedInternally = 0x00002000, iUsingMentor = 0x00004000, iResolveNamesInTemplate = 0x00008000, iDetaching = 0x00010000, iNeedsCollectionView = 0x00020000, iInPriorityBindingExpression= 0x00040000, iInMultiBindingExpression = 0x00080000, iUsingFallbackValue = 0x00100000, iNotifyOnValidationError = 0x00200000, iAttaching = 0x00400000, iNotifyOnSourceUpdated = 0x00800000, iValidatesOnExceptions = 0x01000000, iValidatesOnDataErrors = 0x02000000, iIllegalInput = 0x04000000, iNeedsValidation = 0x08000000, iPropagationMask = iSourceToTarget | iTargetToSource | iPropDefault, iUpdateMask = iUpdateOnLostFocus | iUpdateExplicitly, } //----------------------------------------------------- // // Constructors // //----------------------------------------------------- ///Constructor internal BindingExpressionBase(BindingBase binding, BindingExpressionBase parent) : base(ExpressionMode.SupportsUnboundSources) { if (binding == null) throw new ArgumentNullException("binding"); _binding = binding; _parentBindingExpression = parent; _flags = (PrivateFlags)binding.Flags; if (parent != null) { ResolveNamesInTemplate = parent.ResolveNamesInTemplate; Type type = parent.GetType(); if (type == typeof(MultiBindingExpression)) ChangeFlag(PrivateFlags.iInMultiBindingExpression, true); else if (type == typeof(PriorityBindingExpression)) ChangeFlag(PrivateFlags.iInPriorityBindingExpression, true); } // initialize tracing information PresentationTraceLevel traceLevel = PresentationTraceSources.GetTraceLevel(binding); if (traceLevel > 0) { // copy TraceLevel from parent BindingBase - it can be changed later PresentationTraceSources.SetTraceLevel(this, traceLevel); } if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.CreateExpression)) { if (parent == null) { TraceData.Trace(TraceEventType.Warning, TraceData.CreatedExpression( TraceData.Identify(this), TraceData.Identify(binding))); } else { TraceData.Trace(TraceEventType.Warning, TraceData.CreatedExpressionInParent( TraceData.Identify(this), TraceData.Identify(binding), TraceData.Identify(parent))); } } if (LookupValidationRule(typeof(ExceptionValidationRule)) != null) { ChangeFlag(PrivateFlags.iValidatesOnExceptions, true); } if (LookupValidationRule(typeof(DataErrorValidationRule)) != null) { ChangeFlag(PrivateFlags.iValidatesOnDataErrors, true); } } //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- ///Binding from which this expression was created public BindingBase ParentBindingBase { get { return _binding; } } ///Status of the BindingExpression public BindingStatus Status { get { return _status; } } ////// The ValidationError that caused this /// BindingExpression to be invalid. /// public virtual ValidationError ValidationError { get { return _validationError; } } ////// HasError returns true if any of the ValidationRules /// in the ParentBinding failed its validation rule. /// public virtual bool HasError { get { return _validationError != null; } } //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ ///Force a data transfer from source to target public virtual void UpdateTarget() { } ///Send the current value back to the source ///Does nothing when binding's Mode is not TwoWay or OneWayToSource public virtual void UpdateSource() { } #region Expression overrides ////// Notification that the Expression has been set as a property's value /// ////// Subclasses should not override OnAttach(), but must override Attach() /// /// DependencyObject being set /// Property being set internal sealed override void OnAttach(DependencyObject d, DependencyProperty dp) { if (d == null) throw new ArgumentNullException("d"); if (dp == null) throw new ArgumentNullException("dp"); Attach(d, dp); } ////// Notification that the Expression has been removed as a property's value /// ////// Subclasses should not override OnDetach(), but must override Detach() /// /// DependencyObject being cleared /// Property being cleared internal sealed override void OnDetach(DependencyObject d, DependencyProperty dp) { Detach(); } ////// List of sources of the Expression /// ///Sources list internal override DependencySource[] GetSources() { int j, k; int n = (_sources != null) ? _sources.Length : 0; if (n == 0) return null; DependencySource[] result = new DependencySource[n]; // convert the weak references into strong ones for (j=0, k=0; k/// Handle events from the centralized event table /// Create an untargeted BindingExpression internal static BindingExpressionBase CreateUntargetedBindingExpression(DependencyObject d, BindingBase binding) { return binding.CreateBindingExpression(d, NoTargetProperty); } ///Attach the BindingExpression to its target element ////// This method must be called once during the initialization. /// /// The target element internal void Attach(DependencyObject d) { Attach(d, NoTargetProperty); } ///This event is raised when the BindingExpression's value changes internal event EventHandlerValueChanged; /* The following APIs are also needed for untargeted bindings, but they already exist for other reasons. /// The current value of the BindingExpression internal object Value { get; set; } ///Activate the BindingExpression, using the given item as its root item. internal void Activate(object item) {} ///Deactivate the BindingExpression. internal void Deactivate() {} ///Detach the BindingExpression from its target element ////// This method must be called once when the BindingExpression is no longer needed. /// internal void Detach() {} */ #endregion BindingExpressions with no target DP //----------------------------------------------------- // // Protected Properties // //----------------------------------------------------- ///True if this binding expression is attaching internal bool IsAttaching { get { return TestFlag(PrivateFlags.iAttaching); } set { ChangeFlag(PrivateFlags.iAttaching, value); } } ///True if this binding expression is detaching internal bool IsDetaching { get { return TestFlag(PrivateFlags.iDetaching); } set { ChangeFlag(PrivateFlags.iDetaching, value); } } ///True if this binding expression updates the target internal bool IsDynamic { get { return ( TestFlag(PrivateFlags.iSourceToTarget) && (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsDynamic)); } } ///True if this binding expression updates the source internal bool IsReflective { get { return ( TestFlag(PrivateFlags.iTargetToSource) && (!IsInMultiBindingExpression || ParentMultiBindingExpression.IsReflective)); } set { ChangeFlag(PrivateFlags.iTargetToSource, value); } } ///True if this binding expression uses a default ValueConverter internal bool UseDefaultValueConverter { get { return TestFlag(PrivateFlags.iDefaultValueConverter); } set { ChangeFlag(PrivateFlags.iDefaultValueConverter, value); } } ///True if this binding expression is OneWayToSource internal bool IsOneWayToSource { get { return (_flags & PrivateFlags.iPropagationMask) == PrivateFlags.iTargetToSource; } } ///True if this binding expression updates on PropertyChanged internal bool IsUpdateOnPropertyChanged { get { return (_flags & PrivateFlags.iUpdateMask) == 0; } } ///True if this binding expression updates on LostFocus internal bool IsUpdateOnLostFocus { get { return TestFlag(PrivateFlags.iUpdateOnLostFocus); } } ///True if this binding expression has a pending target update internal bool IsTransferPending { get { return TestFlag(PrivateFlags.iTransferPending); } set { ChangeFlag(PrivateFlags.iTransferPending, value); } } ///True if this binding expression is deferring a target update internal bool TransferIsDeferred { get { return TestFlag(PrivateFlags.iTransferDeferred); } set { ChangeFlag(PrivateFlags.iTransferDeferred, value); } } ///True if this binding expression is updating the target internal bool IsInTransfer { get { return TestFlag(PrivateFlags.iInTransfer); } set { ChangeFlag(PrivateFlags.iInTransfer, value); } } ///True if this binding expression is updating the source internal bool IsInUpdate { get { return TestFlag(PrivateFlags.iInUpdate); } set { ChangeFlag(PrivateFlags.iInUpdate, value); } } ///True if this binding expression is using the fallback value internal bool UsingFallbackValue { get { return TestFlag(PrivateFlags.iUsingFallbackValue); } set { ChangeFlag(PrivateFlags.iUsingFallbackValue, value); } } ///True if this binding expression uses the mentor of the target element internal bool UsingMentor { get { return TestFlag(PrivateFlags.iUsingMentor); } set { ChangeFlag(PrivateFlags.iUsingMentor, value); } } ///True if this binding expression should resolve ElementName within the template of the target element internal bool ResolveNamesInTemplate { get { return TestFlag(PrivateFlags.iResolveNamesInTemplate); } set { ChangeFlag(PrivateFlags.iResolveNamesInTemplate, value); } } ///True if this binding expression has a pending target update internal bool NeedsDataTransfer { get { return TestFlag(PrivateFlags.iNeedDataTransfer); } set { ChangeFlag(PrivateFlags.iNeedDataTransfer, value); } } ///True if this binding expression has a pending source update internal bool NeedsUpdate { get { return TestFlag(PrivateFlags.iNeedUpdate); } set { ChangeFlag(PrivateFlags.iNeedUpdate, value); if (value) { NeedsValidation = true; } } } ///True if this binding expression needs validation internal bool NeedsValidation { get { return TestFlag(PrivateFlags.iNeedsValidation); } set { ChangeFlag(PrivateFlags.iNeedsValidation, value); } } ///True if this binding expression should raise the TargetUpdated event internal bool NotifyOnTargetUpdated { get { return TestFlag(PrivateFlags.iNotifyOnTargetUpdated); } set { ChangeFlag(PrivateFlags.iNotifyOnTargetUpdated, value); } } ///True if this binding expression should raise the SourceUpdated event internal bool NotifyOnSourceUpdated { get { return TestFlag(PrivateFlags.iNotifyOnSourceUpdated); } set { ChangeFlag(PrivateFlags.iNotifyOnSourceUpdated, value); } } ///True if this binding expression should raise the ValidationError event internal bool NotifyOnValidationError { get { return TestFlag(PrivateFlags.iNotifyOnValidationError); } set { ChangeFlag(PrivateFlags.iNotifyOnValidationError, value); } } ///True if this binding expression belongs to a PriorityBinding internal bool IsInPriorityBindingExpression { get { return TestFlag(PrivateFlags.iInPriorityBindingExpression); } } ///True if this binding expression belongs to a MultiBinding internal bool IsInMultiBindingExpression { get { return TestFlag(PrivateFlags.iInMultiBindingExpression); } } ///True if this binding expression belongs to a PriorityBinding or MultiBinding internal bool IsInBindingExpressionCollection { get { return TestFlag(PrivateFlags.iInPriorityBindingExpression | PrivateFlags.iInMultiBindingExpression); } } ///True if this binding expression validates on exceptions internal bool ValidatesOnExceptions { get { return TestFlag(PrivateFlags.iValidatesOnExceptions); } } ///True if this binding expression validates on data errors internal bool ValidatesOnDataErrors { get { return TestFlag(PrivateFlags.iValidatesOnDataErrors); } } ///The parent MultiBindingExpression (if any) internal MultiBindingExpression ParentMultiBindingExpression { get { return _parentBindingExpression as MultiBindingExpression; } } ///The parent PriorityBindingExpression (if any) internal PriorityBindingExpression ParentPriorityBindingExpression { get { return _parentBindingExpression as PriorityBindingExpression; } } ///The parent PriorityBindingExpression or MultiBindingExpression (if any) internal BindingExpressionBase ParentBindingExpressionBase { get { return _parentBindingExpression; } } ///The FallbackValue (from the parent Binding), possibly converted /// to a type suitable for the target property. internal object FallbackValue { // perf note: we recompute the value every time it's needed. This is // a good decision if we seldom need the value. Alternatively we could // cache it. Wait until we know what the perf impact really is. get { return ConvertFallbackValue(ParentBindingBase.FallbackValue, TargetProperty, this); } } ///The default value of the target property internal object DefaultValue { // perf note: we recompute the value every time it's needed. This is // a good decision if we seldom need the value. Alternatively we could // cache it. Wait until we know what the perf impact really is. get { DependencyObject target = TargetElement; if (target != null) { return TargetProperty.GetDefaultValue(target.DependencyObjectType); } return DependencyProperty.UnsetValue; } } ///The effective string format, taking into account the target /// property, parent bindings, convenience syntax, etc. internal string EffectiveStringFormat { get { return _effectiveStringFormat; } } ///The effective TargetNullValue, taking into account the target /// property, parent bindings, etc. internal object EffectiveTargetNullValue { get { return _effectiveTargetNullValue; } } ///return the root of the tree of {Multi/Priority}BindingExpressions internal BindingExpressionBase RootBindingExpression { get { BindingExpressionBase child = this; BindingExpressionBase parent = this.ParentBindingExpressionBase; while (parent != null) { child = parent; parent = child.ParentBindingExpressionBase; } return child; } } ///return the binding group to which this expression belongs (or null) internal BindingGroup BindingGroup { get { BindingExpressionBase root = RootBindingExpression; WeakReference wr = root._bindingGroup; return (wr == null) ? null : (BindingGroup)wr.Target; } } internal virtual bool IsParentBindingUpdateTriggerDefault { get { return false; } } //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ ////// Attach the binding expression to the given target object and property. /// Derived classes should call base.AttachOverride before doing their work. /// internal virtual void AttachOverride(DependencyObject target, DependencyProperty dp) { _targetElement = new WeakReference(target); _targetProperty = dp; // get the engine _engine = DataBindEngine.CurrentDataBindEngine; DetermineEffectiveStringFormat(); DetermineEffectiveTargetNullValue(); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach)) { TraceData.Trace(TraceEventType.Warning, TraceData.AttachExpression( TraceData.Identify(this), target.GetType().FullName, dp.Name, AvTrace.GetHashCodeHelper(target))); } } ////// Detach the binding expression from its target object and property. /// Derived classes should call base.DetachOverride after doing their work. /// internal virtual void DetachOverride() { _engine = null; _targetElement = null; _targetProperty = null; SetStatus(BindingStatus.Detached); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Attach)) { TraceData.Trace(TraceEventType.Warning, TraceData.DetachExpression( TraceData.Identify(this))); } } ////// Invalidate the given child expression. /// internal abstract void InvalidateChild(BindingExpressionBase bindingExpression); ////// Change the dependency sources for the given child expression. /// internal abstract void ChangeSourcesForChild(BindingExpressionBase bindingExpression, WeakDependencySource[] newSources); ////// Replace the given child expression with a new one. /// internal abstract void ReplaceChild(BindingExpressionBase bindingExpression); // handle joining or leaving a binding group internal void OnBindingGroupChanged(bool joining) { if (joining) { // joining a binding group: // update Explicitly, unless declared otherwise if (IsParentBindingUpdateTriggerDefault) { if (IsUpdateOnLostFocus) { LostFocusEventManager.RemoveListener(TargetElement, this); } SetUpdateSourceTrigger(UpdateSourceTrigger.Explicit); } } else { // leaving a binding group: // restore update trigger if (IsParentBindingUpdateTriggerDefault) { FrameworkPropertyMetadata fwMetaData = TargetProperty.GetMetadata(TargetElement.DependencyObjectType) as FrameworkPropertyMetadata; UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData); SetUpdateSourceTrigger(ust); if (IsUpdateOnLostFocus) { LostFocusEventManager.AddListener(TargetElement, this); } } } } // register the leaf bindings with the binding group internal abstract void UpdateBindingGroup(BindingGroup bg); // transfer a value from target to source internal void UpdateValue() { UpdateValidationError(null); object value = GetRawProposedValue(); if (!Validate(value, ValidationStep.RawProposedValue)) return; value = ConvertProposedValue(value); if (!Validate(value, ValidationStep.ConvertedProposedValue)) return; value = UpdateSource(value); if (!Validate(value, ValidationStep.UpdatedValue)) return; value = CommitSource(value); if (!Validate(value, ValidationStep.CommittedValue)) return; EndSourceUpdate(); } ////// Get the raw proposed value /// internal virtual object GetRawProposedValue() { object value = Value; // TargetNullValue is the UI representation of a "null" value. Use null internally. if (Object.Equals(value, EffectiveTargetNullValue)) { value = null; } return value; } /// /// Get the converted proposed value /// internal abstract object ConvertProposedValue(object rawValue); /// /// Get the converted proposed value and inform the binding group /// internal abstract void ObtainConvertedProposedValue(BindingGroup bindingGroup); /// /// Update the source value /// internal abstract object UpdateSource(object convertedValue); /// /// Update the source value and inform the binding group /// internal abstract void UpdateSource(BindingGroup bindingGroup); /// /// Commit the source value /// internal virtual object CommitSource(object value) { return value; } /// /// Store the value in the binding group /// internal abstract void StoreValueInBindingGroup(object value, BindingGroup bindingGroup); ////// Run validation rules for the given step /// internal virtual bool Validate(object value, ValidationStep validationStep) { if (value == Binding.DoNothing || value == DependencyProperty.UnsetValue) return true; // clear errors from this step - we're about to reevaluate those rules ClearValidationErrors(validationStep); Collection validationRules = ParentBindingBase.ValidationRulesInternal; if (validationRules != null) { CultureInfo culture = GetCulture(); switch (validationStep) { case ValidationStep.UpdatedValue: case ValidationStep.CommittedValue: // rules at these steps get passed the rule owner value = this; break; } foreach (ValidationRule validationRule in validationRules) { if (validationRule.ValidationStep == validationStep) { ValidationResult validationResult = validationRule.Validate(value, culture); if (!validationResult.IsValid) { if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.Update)) { TraceData.Trace(TraceEventType.Warning, TraceData.ValidationRuleFailed( TraceData.Identify(this), TraceData.Identify(validationRule))); } UpdateValidationError( new ValidationError(validationRule, this, validationResult.ErrorContent, null)); return false; // kenlai: } } } } return true; } /// /// Run validation rules for the given step, and inform the binding group /// internal abstract bool CheckValidationRules(BindingGroup bindingGroup, ValidationStep validationStep); /// /// Compute the culture, either from the parent Binding, or from the target element. /// internal CultureInfo GetCulture() { // lazy initialization, to let the target element acquire all its properties if (_culture == DefaultValueObject) { // explicit culture set in Binding _culture = ParentBindingBase.ConverterCultureInternal; // if that doesn't work, use target element's xml:lang property if (_culture == null) { DependencyObject target = TargetElement; if (target != null) { if (IsInTransfer && (TargetProperty == FrameworkElement.LanguageProperty)) { // A binding for the Language property needs the value // of the Language property. This circularity is not // supported (bug 1274874). if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Critical, TraceData.RequiresExplicitCulture, TargetProperty.Name, this); } throw new InvalidOperationException(SR.Get(SRID.RequiresExplicitCulture, TargetProperty.Name)); } // cache CultureInfo since requerying an inheritable property on every Transfer/Update can be quite expensive // CultureInfo DP rarely changes once a XAML document is loaded. // To be 100% correct, changes to the CultureInfo attached DP should be tracked // and cause a re-evaluation of this binding. _culture = ((XmlLanguage) target.GetValue(FrameworkElement.LanguageProperty)).GetSpecificCulture(); } } } return (CultureInfo)_culture; } ///Begin a source update internal void BeginSourceUpdate() { ChangeFlag(PrivateFlags.iInUpdate, true); } ///End a source update internal void EndSourceUpdate() { ChangeFlag(PrivateFlags.iInUpdate | PrivateFlags.iNeedUpdate, false); } ///change the value to the new value, and notify listeners internal void ChangeValue(object newValue, bool notify) { object oldValue = (_value != DefaultValueObject) ? _value : DependencyProperty.UnsetValue; _value = newValue; if (notify && ValueChanged != null) { ValueChanged(this, new BindingValueChangedEventArgs(oldValue, newValue)); } } ///the target value has changed - the source needs to be updated internal void Dirty() { if (!IsInTransfer) { NeedsUpdate = true; if (IsUpdateOnPropertyChanged) Update(true); } } ///use the Fallback or Default value, called when a real value is not available internal object UseFallbackValue() { object value = FallbackValue; // if there's a problem with the fallback, use Default instead if (value == DefaultValueObject) { value = DependencyProperty.UnsetValue; } if (value != DependencyProperty.UnsetValue) { UsingFallbackValue = true; } else { // if fallback isn't available, use Default (except when in a binding collection) if (Status == BindingStatus.Active) SetStatus(BindingStatus.UpdateTargetError); if (!IsInBindingExpressionCollection) { value = DefaultValue; if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Information, TraceData.NoValueToTransfer, this); } } } return value; } // determine if the given value is "null" (in a general sense) internal bool IsNullValue(object value) { if (value == null) return true; if (Convert.IsDBNull(value)) return true; if (IsSqlNull(value)) return true; return false; } // return true if the value is null in the SqlTypes sense bool IsSqlNull(object value) { Type type = value.GetType(); return Engine.IsINullable(type) && IsSqlNullImpl(value); } // separate function to avoid loading System.Data unnecessarily [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] static bool IsSqlNullImpl(object value) { System.Data.SqlTypes.INullable nullable = value as System.Data.SqlTypes.INullable; return (nullable != null && nullable.IsNull); } // determine a "null" value appropriate for the given type internal object NullValueForType(Type type) { if (type == null) return null; object sqlNull; if (IsSqlNullableType(type, out sqlNull)) return sqlNull; if (!type.IsValueType) return null; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) return null; return DependencyProperty.UnsetValue; } // return true if the type is nullable in the SqlTypes sense. If so, return the null value. bool IsSqlNullableType(Type type, out object nullValue) { if (Engine.IsINullable(type)) { return IsSqlNullableTypeImpl(type, out nullValue); } else { nullValue = null; return false; } } // separate function to avoid loading System.Data unnecessarily [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] static bool IsSqlNullableTypeImpl(Type type, out object nullValue) { // some SqlTypes are classes with a Null property. Others are structs with a Null field. Try both. System.Reflection.FieldInfo nullField = type.GetField("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy); if (nullField != null) { nullValue = nullField.GetValue(null); return true; } System.Reflection.PropertyInfo nullProperty = type.GetProperty("Null", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.FlattenHierarchy); if (nullProperty != null) { nullValue = nullProperty.GetValue(null, null); return true; } nullValue = null; return false; } internal ValidationRule LookupValidationRule(Type type) { ValidationRule result = ParentBindingBase.GetValidationRule(type); if (result == null && _parentBindingExpression != null) { result = _parentBindingExpression.LookupValidationRule(type); } return result; } // discover the binding group (if any) that this binding should join, // and join it. More precisely, cause the root binding to join. internal void JoinBindingGroup(bool isReflective, DependencyObject contextElement) { BindingGroup bindingGroup = RootBindingExpression.FindBindingGroup(isReflective, contextElement); if (bindingGroup != null) { JoinBindingGroup(bindingGroup, /*explicit*/false); } } // remove the binding from its group internal void LeaveBindingGroup() { BindingExpressionBase root = RootBindingExpression; BindingGroup bg = root.BindingGroup; if (bg != null) { bg.BindingExpressions.Remove(root); root._bindingGroup = null; } } // discover the binding group (if any) that this root binding should join. BindingGroup FindBindingGroup(bool isReflective, DependencyObject contextElement) { Debug.Assert(RootBindingExpression == this, "Only call this with a root binding"); // if we've already joined (or failed to join) a group, just return the result if (_bindingGroup != null) { return (BindingGroup)_bindingGroup.Target; } BindingGroup bg; string groupName = ParentBindingBase.BindingGroupName; // a null group name means "don't join any group". if (groupName == null) { MarkAsNonGrouped(); return null; } // an empty group name means join by DataContext if (String.IsNullOrEmpty(groupName)) { // check further preconditions: if (!isReflective || // must have target-to-source data flow contextElement == null) // must use data context { // later child bindings might pass this test, so don't mark // this root binding as non-grouped yet return null; } // only the innermost binding group is eligible bg = (BindingGroup)contextElement.GetValue(FrameworkElement.BindingGroupProperty); if (bg == null) { MarkAsNonGrouped(); return null; } // the context element must share data context with the group DependencyProperty dataContextDP = FrameworkElement.DataContextProperty; DependencyObject groupContextElement = bg.InheritanceContext; if (groupContextElement == null || !Object.Equals( contextElement.GetValue(dataContextDP), groupContextElement.GetValue(dataContextDP))) { MarkAsNonGrouped(); return null; } // if the binding survives the gauntlet, return the group return bg; } // a non-empty group name means join by name else { // walk up the tree, looking for a matching binding group DependencyProperty bindingGroupDP = FrameworkElement.BindingGroupProperty; FrameworkObject fo = new FrameworkObject(TargetElement); while (fo.DO != null) { BindingGroup bgCandidate = (BindingGroup)fo.DO.GetValue(bindingGroupDP); if (bgCandidate == null) { MarkAsNonGrouped(); return null; } if (bgCandidate.Name == groupName) { // return the matching group return bgCandidate; } fo = fo.FrameworkParent; } // no match - report an error if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.BindingGroupNameMatchFailed(groupName), this); } MarkAsNonGrouped(); return null; } } // add a binding to the given binding group internal void JoinBindingGroup(BindingGroup bg, bool explicitJoin) { BindingExpressionBase root = null; // set to non-null by the next loop for ( BindingExpressionBase bindingExpr = this; bindingExpr != null; bindingExpr = bindingExpr.ParentBindingExpressionBase) { root = bindingExpr; // bindings in a group update Explicitly, unless declared otherwise bindingExpr.OnBindingGroupChanged(/*joining*/true); bg.AddToValueTable(bindingExpr); } // add the root binding to the group if (root._bindingGroup == null) { // use WeakReference because the BindingGroup contains a strong reference // to the visual tree (via InheritanceContext) root._bindingGroup = new WeakReference(bg); // when the group is implicitly discovered, always add the root binding to the group's collection. // When the binding is added explicitly - via BindingGroup.BindingExpressions.Add() - // check first to see if it has already been added bool addToGroup = explicitJoin ? !bg.BindingExpressions.Contains(root) : true; if (addToGroup) { bg.BindingExpressions.Add(root); } // in the explicit case, register its items and values with the binding group // (the implicit case does this when the bindings activate) if (explicitJoin) { root.UpdateBindingGroup(bg); } } else { if (root.BindingGroup != bg) throw new InvalidOperationException(SR.Get(SRID.BindingGroup_CannotChangeGroups)); } } // mark a binding as non-grouped, so that we avoid doing the discovery again void MarkAsNonGrouped() { // Leaf bindings only get asked once, so there's no need to add a mark if (!(this is BindingExpression)) { _bindingGroup = new WeakReference(null); } } ////// Handle events from the centralized event table /// internal virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e) { return false; // unrecognized event } private bool TestFlag(PrivateFlags flag) { return (_flags & flag) != 0; } private void ChangeFlag(PrivateFlags flag, bool value) { if (value) _flags |= flag; else _flags &= ~flag; } //----------------------------------------------------- // // Internal Properties // //------------------------------------------------------ internal DependencyProperty TargetProperty { get { return _targetProperty; } } // A BindingExpression cannot hold a strong reference to the target element - this // leads to memory leaks (bug 871139). The problem is that BindingExpression and its workers // register for events from the data item, creating a reference from // the data item to the BindingExpression. The data item typically has a long lifetime, // so if the BindingExpression held a SR to the target, the target element would // also stay alive forever. // Instead, BindingExpression holds a WeakReference to the target. This means we // have to check it before dereferencing (here), and cope when the // reference fails (in callers to this property). Requests for the TargetElement // are not trivial, so callers should request it once and cache the result // in a local variable. They should not save it in a global or instance // variable of course; that would defeat the purpose of the WR. // This allows the target element to be GC'd when it's no longer in // use by the tree or application. The next time the BindingExpression asks for // its TargetElement, the WR will fail. At this point, the BindingExpression is no // longer useful, so it can sever all connections to the outside world (i.e. // stop listening for events). This allows the BindingExpression itself to be GC'd. internal DependencyObject TargetElement { get { if (_targetElement != null) { DependencyObject result = _targetElement.Target as DependencyObject; if (result != null) return result; // target has been GC'd, sever all connections _targetElement = null; // prevents re-entry from Detach() Detach(); } return null; } } internal WeakReference TargetElementReference { get { return _targetElement; } } internal DataBindEngine Engine { get { return _engine; } } internal Dispatcher Dispatcher { get { return (_engine != null) ? _engine.Dispatcher : null; } } internal object Value { get { if (_value == DefaultValueObject) { // don't notify listeners. This isn't a real value change. ChangeValue(UseFallbackValue(), false /*notify*/); } return _value; } set { ChangeValue(value, true); Dirty(); } } internal WeakDependencySource[] WeakSources { get { return _sources; } } ////// NoTarget DependencyProperty, a placeholder used by BindingExpressions with no target property /// internal static readonly DependencyProperty NoTargetProperty = DependencyProperty.RegisterAttached("NoTarget", typeof(object), typeof(BindingExpressionBase), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None)); internal TraceLog TraceLog { get { return _traceLog; } } //------------------------------------------------------ // // Internal Methods // //----------------------------------------------------- ////// Attach the binding expression to the given target object and property. /// internal void Attach(DependencyObject target, DependencyProperty dp) { // make sure we're on the right thread to access the target if (target != null) { target.VerifyAccess(); } IsAttaching = true; AttachOverride(target, dp); IsAttaching = false; } ////// Detach the binding expression from its target object and property. /// internal void Detach() { if (_status == BindingStatus.Detached || IsDetaching) return; IsDetaching = true; DetachOverride(); IsDetaching = false; } internal void SetStatus(BindingStatus status) { if (_status == BindingStatus.Detached && status != _status) { throw new InvalidOperationException(SR.Get(SRID.BindingExpressionStatusChanged, _status, status)); } _status = status; if (_traceLog != null) { _traceLog.Add("Set status = {0} {1}", _status, new StackTrace()); } } // convert a user-supplied fallback value to a usable equivalent // returns: UnsetValue if user did not supply a fallback value // value if fallback value is legal // DefaultValueObject otherwise internal static object ConvertFallbackValue(object value, DependencyProperty dp, object sender) { Exception e; object result = ConvertValue(value, dp, out e); if (result == DefaultValueObject) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.FallbackConversionFailed( AvTrace.ToStringHelper(value), AvTrace.TypeName(value), dp.Name, dp.PropertyType.Name), sender, e); } } return result; } // convert a user-supplied TargetNullValue to a usable equivalent // returns: UnsetValue if user did not supply a fallback value // value if fallback value is legal // DefaultValueObject otherwise internal static object ConvertTargetNullValue(object value, DependencyProperty dp, object sender) { Exception e; object result = ConvertValue(value, dp, out e); if (result == DefaultValueObject) { if (TraceData.IsEnabled) { TraceData.Trace(TraceEventType.Error, TraceData.TargetNullValueConversionFailed( AvTrace.ToStringHelper(value), AvTrace.TypeName(value), dp.Name, dp.PropertyType.Name), sender, e); } } return result; } static object ConvertValue(object value, DependencyProperty dp, out Exception e) { object result; e = null; if (value == DependencyProperty.UnsetValue || dp.IsValidValue(value)) { result = value; } else { result = null; // placeholder to keep compiler happy // if value isn't the right type, use a type converter to get a better value bool success = false; TypeConverter converter = DefaultValueConverter.GetConverter(dp.PropertyType); if (converter != null && converter.CanConvertFrom(value.GetType())) { // PreSharp uses message numbers that the C# compiler doesn't know about. // Disable the C# complaints, per the PreSharp documentation. #pragma warning disable 1634, 1691 // PreSharp complains about catching NullReference (and other) exceptions. // It doesn't recognize that IsCriticalException() handles these correctly. #pragma warning disable 56500 try { result = converter.ConvertFrom(null, CultureInfo.InvariantCulture, value); success = dp.IsValidValue(result); } // Catch all exceptions. If we can't convert the fallback value, it doesn't // matter why not; we should always use the default value instead. // (See bug 1853628 for an example of a converter that throws // an exception not mentioned in the documentation for ConvertFrom.) catch (Exception ex) { e = ex; } catch // non CLS compliant exception { } #pragma warning restore 56500 #pragma warning restore 1634, 1691 } if (!success) { // if can't convert it, don't use it result = DefaultValueObject; } } return result; } // Certain trace reports should be marked as 'error' unless the binding // is prepared to handle it in some way (e.g. FallbackValue), in which // case 'warning'. internal TraceEventType TraceLevel { get { // FallbackValue is present if (ParentBindingBase.FallbackValue != DependencyProperty.UnsetValue) return TraceEventType.Warning; // Binding is a member of MultiBinding or PriorityBinding if (IsInBindingExpressionCollection) return TraceEventType.Warning; // all other cases - error return TraceEventType.Error; } } internal virtual void Activate() { } internal virtual void Deactivate() { } internal virtual void Update(bool synchronous) { } internal void UpdateValidationError(ValidationError validationError) { if (_validationError != null) { ValidationError oldValidationError = _validationError; _validationError = null; // clear before raising events, so that HasError is correct RemoveValidationError(oldValidationError); } _validationError = validationError; if (_validationError != null) { AddValidationError(_validationError); } } internal void AddValidationError(ValidationError validationError) { // add the error to the target element Validation.AddValidationError(validationError, TargetElement, NotifyOnValidationError); // add the error to the binding group's target element BindingGroup bindingGroup = BindingGroup; if (bindingGroup != null) { bindingGroup.AddValidationError(validationError); } } internal void RemoveValidationError(ValidationError validationError) { // remove the error from the target element Validation.RemoveValidationError(validationError, TargetElement, NotifyOnValidationError); // remove the error from the binding group's target element BindingGroup bindingGroup = BindingGroup; if (bindingGroup != null) { bindingGroup.RemoveValidationError(validationError); } } // remove all errors raised at the given step, in preparation for running // the rules at that step internal void ClearValidationErrors(ValidationStep validationStep) { if (_validationError == null || _validationError.RuleInError.ValidationStep != validationStep) return; RemoveValidationError(_validationError); _validationError = null; } internal void ChangeSources(WeakDependencySource[] newSources) { if (IsInBindingExpressionCollection) ParentBindingExpressionBase.ChangeSourcesForChild(this, newSources); else ChangeSources(TargetElement, TargetProperty, newSources); // store the sources with weak refs, so they don't cause memory leaks (bug 980041) _sources = newSources; } ////// combine the sources of BindingExpressions, using new sources for /// the BindingExpression at the given index /// /// -1 to indicate no new sources /// collection of child binding expressions /// how many child expressions to include /// use null when no new sources ///internal static WeakDependencySource[] CombineSources(int index, Collection bindingExpressions, int count, WeakDependencySource[] newSources) { if (index == count) { // Be sure to include newSources if they are being appended count++; } Collection tempList = new Collection (); for (int i = 0; i < count; ++i) { BindingExpressionBase bindExpr = bindingExpressions[i]; WeakDependencySource[] sources = (i==index) ? newSources : (bindExpr != null) ? bindExpr.WeakSources : null; int m = (sources == null) ? 0 : sources.Length; for (int j = 0; j < m; ++j) { WeakDependencySource candidate = sources[j]; // don't add duplicate source for (int k = 0; k < tempList.Count; ++k) { WeakDependencySource prior = tempList[k]; if (candidate.DependencyObject == prior.DependencyObject && candidate.DependencyProperty == prior.DependencyProperty) { candidate = null; break; } } if (candidate != null) tempList.Add(candidate); } } WeakDependencySource[] result; if (tempList.Count > 0) { result = new WeakDependencySource[tempList.Count]; tempList.CopyTo(result, 0); tempList.Clear(); } else { result = null; } return result; } internal void ResolvePropertyDefaultSettings(BindingMode mode, UpdateSourceTrigger updateTrigger, FrameworkPropertyMetadata fwMetaData) { // resolve "property-default" dataflow if (mode == BindingMode.Default) { BindingFlags f = BindingFlags.OneWay; if (fwMetaData != null && fwMetaData.BindsTwoWayByDefault) { f = BindingFlags.TwoWay; } ChangeFlag(PrivateFlags.iPropagationMask, false); ChangeFlag((PrivateFlags)f, true); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults)) { TraceData.Trace(TraceEventType.Warning, TraceData.ResolveDefaultMode( TraceData.Identify(this), (f == BindingFlags.OneWay) ? BindingMode.OneWay : BindingMode.TwoWay)); } } Debug.Assert((_flags & PrivateFlags.iPropagationMask) != PrivateFlags.iPropDefault, "BindingExpression should not have Default propagation"); // resolve "property-default" update trigger if (updateTrigger == UpdateSourceTrigger.Default) { UpdateSourceTrigger ust = GetDefaultUpdateSourceTrigger(fwMetaData); SetUpdateSourceTrigger(ust); if (TraceData.IsExtendedTraceEnabled(this, TraceDataLevel.ResolveDefaults)) { TraceData.Trace(TraceEventType.Warning, TraceData.ResolveDefaultUpdate( TraceData.Identify(this), ust)); } } Invariant.Assert((_flags & PrivateFlags.iUpdateMask) != PrivateFlags.iUpdateDefault, "BindingExpression should not have Default update trigger"); } // return the effective update trigger, used when binding doesn't set one explicitly internal UpdateSourceTrigger GetDefaultUpdateSourceTrigger(FrameworkPropertyMetadata fwMetaData) { UpdateSourceTrigger ust = IsInMultiBindingExpression ? UpdateSourceTrigger.Explicit : (fwMetaData != null) ? fwMetaData.DefaultUpdateSourceTrigger : UpdateSourceTrigger.PropertyChanged; return ust; } internal void SetUpdateSourceTrigger(UpdateSourceTrigger ust) { ChangeFlag(PrivateFlags.iUpdateMask, false); ChangeFlag((PrivateFlags)BindingBase.FlagsFrom(ust), true); } internal void DetermineEffectiveStringFormat() { Type targetType = TargetProperty.PropertyType; if (targetType != typeof(String)) { // if the target type isn't String, we don't need a string format return; // _effectiveStringFormat is already null } // determine the effective target type and the declared string format // by looking up the tree of binding expressions string stringFormat = ParentBindingBase.StringFormat; BindingExpressionBase be = this.ParentBindingExpressionBase; while (be != null) { if (be is MultiBindingExpression) { // MultiBindings should receive object values, not string targetType = typeof(Object); break; } else if (stringFormat == null && be is PriorityBindingExpression) { // use a PriorityBinding's string format, unless we already // have a more specific one stringFormat = be.ParentBindingBase.StringFormat; } be = be.ParentBindingExpressionBase; } // if we need a string format, cache it if (targetType == typeof(String)) { #if NotToday // special case: when these conditions all apply // a) target element belongs to a DataTemplate // b) template was found by implicit (type-based) lookup // c) container (ContentPresenter) has a string format // d) binding has no effective string format // then use the CP's string format. This enables scenarios like // // // // where you'd like to control the format at the point of use, // rather than at the point of template declaration. This is // especially useful when the template is declared in a global place // like app resources or a theme file. In particular it makes the // GroupStyle.HeaderStringFormat property work; the template for // GroupItem is defined in the theme, but needs to pick up formatting // from the app's markup. if (stringFormat == null) // (d) { FrameworkObject fo = new FrameworkObject(TargetElement); ContentPresenter cp = fo.TemplatedParent as ContentPresenter; if (cp != null && // (a) cp.ContentStringFormat != null) // (c) { DataTemplate dt = cp.TemplateInternal as DataTemplate; if (dt != null && // (a) dt.DataType != null) // (b) { stringFormat = cp.ContentStringFormat; } } } #endif if (!String.IsNullOrEmpty(stringFormat)) { _effectiveStringFormat = Helper.GetEffectiveStringFormat(stringFormat); } } } internal void DetermineEffectiveTargetNullValue() { Type targetType = TargetProperty.PropertyType; // determine the effective target type and the declared TargetNullValue // by looking up the tree of binding expressions object targetNullValue = ParentBindingBase.TargetNullValue; BindingExpressionBase be = this.ParentBindingExpressionBase; while (be != null) { if (be is MultiBindingExpression) { // MultiBindings should receive object values targetType = typeof(Object); break; } else if (targetNullValue == DependencyProperty.UnsetValue && be is PriorityBindingExpression) { // use a PriorityBinding's TargetNullValue, unless we already // have a more specific one targetNullValue = be.ParentBindingBase.TargetNullValue; } be = be.ParentBindingExpressionBase; } // if user declared a TargetNullValue, make sure it has the right type. if (targetNullValue != DependencyProperty.UnsetValue) { targetNullValue = ConvertTargetNullValue(targetNullValue, TargetProperty, this); if (targetNullValue == DefaultValueObject) { // if not, ignore it (having logged a trace message) targetNullValue = DependencyProperty.UnsetValue; } } // for back-compat, don't turn on TargetNullValue unless user explicitly // asks for it. This means users have to add TargetNullValue to get // pretty basic scenarios to work (e.g. binding a TextBox to a database // string field that supports DBNull). It's painful, but can't be // helped. #if TargetNullValueBC //BreakingChange // if no declared (or poorly declared) value, make one up if (targetNullValue == DependencyProperty.UnsetValue) { targetNullValue = NullValueForType(targetType); } #endif _effectiveTargetNullValue = targetNullValue; } // To prevent memory leaks, we store WeakReferences to certain objects // in various places: _dataItem, _sources, worker fields. The logic // for this is centralized in these two static methods. (See bug 940041) internal static object CreateReference(object item) { // One source of leaks is the reference cycle formed when a BindingExpression's // source item contains a reference chain to the target element: // target -> BindingExpression -> source item -> target // // Making the second link into a WeakReference incurs some cost, // so it should be avoided if we know the third link never exists. // We definitely can't avoid this when the item is a DependencyObject, // since it's reasonably common for the third link to occur (e.g. // a ContextMenu contains a link to its Popup, which has a property // bound back to the ContextMenu). // // For safety, we choose to use WeakRef all the time, unless the item is null. // Exception (bug 1124954): Keep a strong reference to // BindingListCollectionView - this keeps the underlying DataView // alive, when nobody else will. // Exception (bug 1970505): Don't allocate a WeakRef for the common // case of the NullDataItem if (item != null && !(item is BindingListCollectionView) && !(item == BindingExpression.NullDataItem)) { item = new WeakReference(item); } #if USE_ITEM_REFERENCE item = new ItemReference(item); #endif return item; } // like CreateReference, but use an existing WeakReference internal static object CreateReference(WeakReference item) { object result = item; #if USE_ITEM_REFERENCE result = new ItemReference(item); #endif return result; } // like CreateReference, except re-target the old WeakReference (if any) internal static object ReplaceReference(object oldReference, object item) { if (item != null && !(item is BindingListCollectionView) && !(item == BindingExpression.NullDataItem)) { #if USE_ITEM_REFERENCE // if this cast fails, it's because you have done a direct assignment of an // item to some field instead of assigning the result of CreateReference. oldReference = ((ItemReference)oldReference).Item; #endif WeakReference wr = oldReference as WeakReference; if (wr != null) { wr.Target = item; item = wr; } else { item = new WeakReference(item); } } #if USE_ITEM_REFERENCE item = new ItemReference(item); #endif return item; } internal static object GetReference(object reference) { if (reference == null) return null; #if USE_ITEM_REFERENCE // if this cast fails, it's because you have done a direct assignment of an // item to some field instead of assigning the result of CreateReference. reference = ((ItemReference)reference).Item; #endif WeakReference wr = reference as WeakReference; if (wr != null) return wr.Target; else return reference; } internal static void InitializeTracing(BindingExpressionBase expr, DependencyObject d, DependencyProperty dp) { BindingBase parent = expr.ParentBindingBase; } //------------------------------------------------------ // // Private Methods // //----------------------------------------------------- #if USE_ITEM_REFERENCE private class ItemReference { internal ItemReference(object item) { _item = item; } internal object Item { get { return _item; } } object _item; } #endif // change WeakDependencySources to (strong) DependencySources, and notify // the property engine about the new sources void ChangeSources(DependencyObject target, DependencyProperty dp, WeakDependencySource[] newSources) { DependencySource[] sources; if (newSources != null) { // convert weak reference to strong sources = new DependencySource[newSources.Length]; int n = 0; for (int i = 0; i < newSources.Length; ++i) { DependencyObject sourceDO = newSources[i].DependencyObject; if (sourceDO != null) { // only include sources that are still alive sources[n++] = new DependencySource(sourceDO, newSources[i].DependencyProperty); } } // if any of the sources were no longer alive, trim the array if (n < newSources.Length) { DependencySource[] temp; if (n > 0) { temp = new DependencySource[n]; Array.Copy(sources, 0, temp, 0, n); } else { temp = null; } sources = temp; } } else { sources = null; } // notify property engine ChangeSources(target, dp, sources); } // this method is here just to avoid the compiler error // error CS0649: Warning as Error: Field 'System.Windows.Data.BindingExpression._traceLog' is never assigned to, and will always have its default value null void InitializeTraceLog() { _traceLog = new TraceLog(20); } //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- BindingBase _binding; WeakReference _targetElement; DependencyProperty _targetProperty; DataBindEngine _engine; PrivateFlags _flags; BindingExpressionBase _parentBindingExpression; object _value = DefaultValueObject; BindingStatus _status; TraceLog _traceLog; ValidationError _validationError; WeakDependencySource[] _sources; string _effectiveStringFormat; object _effectiveTargetNullValue; WeakReference _bindingGroup; object _culture = DefaultValueObject; /// Sentinel meaning "field has its default value" internal static readonly object DefaultValueObject = new object(); } } // 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
- KnownBoxes.cs
- Assembly.cs
- CheckBoxFlatAdapter.cs
- DelegateArgumentReference.cs
- PagerSettings.cs
- CacheOutputQuery.cs
- DataSetSchema.cs
- Preprocessor.cs
- DataList.cs
- XmlUtil.cs
- LinqDataSourceHelper.cs
- TrackingProfileManager.cs
- FilteredXmlReader.cs
- XamlWriter.cs
- JsonServiceDocumentSerializer.cs
- ConfigurationConverterBase.cs
- TableItemStyle.cs
- OrderPreservingMergeHelper.cs
- WmlImageAdapter.cs
- FeatureSupport.cs
- ManagementQuery.cs
- ObjectConverter.cs
- CatchBlock.cs
- DataGridViewDataConnection.cs
- HMACSHA384.cs
- CompoundFileStreamReference.cs
- SqlClientPermission.cs
- UserNamePasswordValidationMode.cs
- ObjectReferenceStack.cs
- UncommonField.cs
- FixUp.cs
- Triplet.cs
- basecomparevalidator.cs
- FtpWebResponse.cs
- ArrayTypeMismatchException.cs
- RequestCacheManager.cs
- sqlnorm.cs
- TCEAdapterGenerator.cs
- DbParameterCollectionHelper.cs
- RecognizerBase.cs
- PageVisual.cs
- StreamResourceInfo.cs
- RegistryDataKey.cs
- DeadCharTextComposition.cs
- ActionFrame.cs
- Lookup.cs
- WebBaseEventKeyComparer.cs
- ResourceReferenceExpressionConverter.cs
- TypeDependencyAttribute.cs
- BufferedStream.cs
- UIEndRequest.cs
- AuthenticationModuleElement.cs
- IndexOutOfRangeException.cs
- XmlChildEnumerator.cs
- FlowNode.cs
- EntityDataReader.cs
- DataGrid.cs
- FontInfo.cs
- _HelperAsyncResults.cs
- QilFunction.cs
- AddressingProperty.cs
- HtmlFormWrapper.cs
- EntityTransaction.cs
- Int32Rect.cs
- TransactionState.cs
- TextEditorSpelling.cs
- BamlRecordHelper.cs
- DocumentCollection.cs
- WebPartConnectionsConnectVerb.cs
- StaticExtension.cs
- TablePattern.cs
- ObservableCollection.cs
- ProcessInfo.cs
- CombinedGeometry.cs
- CodeDomDesignerLoader.cs
- SQLRoleProvider.cs
- HttpSessionStateBase.cs
- XmlSchemaDocumentation.cs
- SmtpCommands.cs
- XmlSerializerAssemblyAttribute.cs
- SectionUpdates.cs
- DependencyPropertyKey.cs
- DataGridViewIntLinkedList.cs
- ProtocolsSection.cs
- RepeatInfo.cs
- precedingquery.cs
- Selection.cs
- WrapperSecurityCommunicationObject.cs
- DataListItemCollection.cs
- DesignRelationCollection.cs
- RectAnimationUsingKeyFrames.cs
- XmlSchemaGroupRef.cs
- TrackBarRenderer.cs
- sqlpipe.cs
- XmlIncludeAttribute.cs
- DrawListViewSubItemEventArgs.cs
- sqlstateclientmanager.cs
- ZoneMembershipCondition.cs
- ForeignKeyConstraint.cs
- AnonymousIdentificationSection.cs