BindingExpressionBase.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Data / BindingExpressionBase.cs / 8 / BindingExpressionBase.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: Defines BindingExpressionBase object, 
//              base class for BindingExpression, PriorityBindingExpression, 
//              and MultiBindingExpression.
// 
// See spec at [....]/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;   // Collection
using 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               = 0x40000000, 

            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)
            { 
                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 EventHandler ValueChanged; 

        /* 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); } 
        } 

        ///  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;
            }
        } 

        //----------------------------------------------------- 
        // 
        //  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; 

            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);

        ///  
        /// 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;
        } 
 
        internal ValidationRule LookupValidationRule(Type type)
        { 
            ValidationRule result = ParentBindingBase.GetValidationRule(type);

            if (result == null && _parentBindingExpression != null)
            { 
                result = _parentBindingExpression.LookupValidationRule(type);
            } 
 
            return result;
        } 

        /// 
        /// 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) 
        {
            object result; 
 
            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; 
                Exception e = null;
                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; 

                    if (TraceData.IsEnabled)
                    {
                        TraceData.Trace(TraceEventType.Error, 
                                TraceData.FallbackConversionFailed(
                                    AvTrace.ToStringHelper(value), 
                                    AvTrace.TypeName(value), 
                                    dp.Name,
                                    dp.PropertyType.Name), 
                                sender, e);
                    }
                }
            } 

            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)
        { 
            DependencyObject targetElement = TargetElement; 
            bool shouldRaiseEvent = NotifyOnValidationError;
            ValidationError oldValidationError = _validationError; 

            if (targetElement != null)
            {
                ValidationErrorCollection validationErrors = Validation.GetErrorsInternal(targetElement); 
                bool oldInvalid = (validationErrors != null && validationErrors.Count > 0);
 
                // If there is currently a _validationError, then we will be clearing 
                // it (at least temporarily).
                if (_validationError != null) 
                {
                    Invariant.Assert(validationErrors != null && validationErrors.Count > 0,
                        "The validationErrors collection should not be either null or empty if there is a BindingExpression with a ValidationError");
 
                    if (validationErrors.Count > 0)
                    { 
                        validationErrors.Remove(_validationError); 
                        _validationError = null;
 
                        // if the count is 0 here, then that
                        // means that we have gone from invalid to valid.
                        if (validationErrors.Count == 0)
                        { 
                            targetElement.ClearValue(Validation.HasErrorPropertyKey);
                        } 
 
                        if (shouldRaiseEvent)
                        { 
                            OnValidationError(targetElement, oldValidationError, ValidationErrorEventAction.Removed);
                        }
                    }
                } 

                if (validationError != null) 
                { 
                    if (validationErrors == null)
                    { 
                        validationErrors = new ValidationErrorCollection();
                        validationErrors.Add(validationError);
                        targetElement.SetValue(Validation.ValidationErrorsInternalProperty, validationErrors);
                    } 
                    else
                    { 
                        validationErrors.Add(validationError); 
                    }
 
                    _validationError = validationError;

                    // if the count is 1 here, then that
                    // means that we have gone from valid to invalid. 
                    if (validationErrors.Count == 1)
                    { 
                        targetElement.SetValue(Validation.HasErrorPropertyKey, BooleanBoxes.TrueBox); 
                    }
 
                    if (shouldRaiseEvent)
                    {
                        OnValidationError(targetElement, _validationError, ValidationErrorEventAction.Added);
                    } 
                }
 
                bool invalid = (validationErrors != null && validationErrors.Count > 0); 
                if (!invalid && oldInvalid)
                { 
                    targetElement.ClearValue(Validation.ValidationErrorsInternalProperty);
                }

                if (invalid || oldInvalid) 
                {
                    Validation.ShowValidationAdorner(targetElement, invalid); 
                } 
            }
 
            GC.KeepAlive(targetElement);   // keep target alive during activation (bug 956831)

        }
 
        internal static void OnValidationError(DependencyObject source, ValidationError validationError, ValidationErrorEventAction action)
        { 
            ValidationErrorEventArgs args = new ValidationErrorEventArgs(validationError, action); 

            if (source is ContentElement) 
                ((ContentElement)source).RaiseEvent(args);
            else if (source is UIElement)
                ((UIElement)source).RaiseEvent(args);
            else if (source is UIElement3D) 
                ((UIElement3D)source).RaiseEvent(args);
        } 
 
        internal void ChangeSources(WeakDependencySource[] newSources)
        { 
            DependencyObject target = TargetElement;
            if (target != null)
            {
                if (IsInBindingExpressionCollection) 
                    ParentBindingExpressionBase.ChangeSourcesForChild(this, newSources);
                else 
                    ChangeSources(target, 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 = 
                    IsInMultiBindingExpression ? UpdateSourceTrigger.Explicit : 
                    (fwMetaData != null) ? fwMetaData.DefaultUpdateSourceTrigger :
                                        UpdateSourceTrigger.PropertyChanged; 

                ChangeFlag(PrivateFlags.iUpdateMask, false);
                ChangeFlag((PrivateFlags)BindingBase.FlagsFrom(ust), true);
 
                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"); 
        } 

 
        // 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; 
 
        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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK