BindingGroup.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / System / Windows / Data / BindingGroup.cs / 1 / BindingGroup.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: Defines BindingGroup object, manages a collection of bindings. 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Collections;               // IList
using System.Collections.Generic;       // IList
using System.Collections.ObjectModel;   // Collection 
using System.Collections.Specialized;   // INotifyCollectionChanged
using System.ComponentModel;            // IEditableObject 
using System.Diagnostics;               // Debug 
using System.Globalization;             // CultureInfo
 
using System.Windows;
using System.Windows.Controls;          // ValidationRule
using MS.Internal.Controls;             // ValidationRuleCollection
using MS.Internal;                      // InheritanceContextHelper 

namespace System.Windows.Data 
{ 
    /// 
    /// A BindingGroup manages a collection of bindings, and provides services for 
    /// item-level and cross-binding validation.
    /// 
    public class BindingGroup : DependencyObject
    { 
        #region Constructors
 
        //----------------------------------------------------- 
        //
        //  Constructors 
        //
        //-----------------------------------------------------

        ///  
        ///     Initializes a new instance of the BindingGroup class.
        ///  
        public BindingGroup() 
        {
            _validationRules = new ValidationRuleCollection(); 
            Initialize();
        }

        // clone the binding group.  Called when setting a binding group on a 
        // container, from the ItemControl's ItemBindingGroup.
        internal BindingGroup(BindingGroup master) 
        { 
            _validationRules = master._validationRules;
            _name = master._name; 
            _notifyOnValidationError = master._notifyOnValidationError;
            Initialize();
        }
 
        void Initialize()
        { 
            _bindingExpressions = new BindingExpressionCollection(); 
            ((INotifyCollectionChanged)_bindingExpressions).CollectionChanged += new NotifyCollectionChangedEventHandler(OnBindingsChanged);
 
            _itemsRW = new Collection();
            _items = new WeakReadOnlyCollection(_itemsRW);
        }
 

        #endregion Constructors 
 
        #region Public properties
 
        //------------------------------------------------------
        //
        //  Public properties
        // 
        //-----------------------------------------------------
 
        ///  
        /// The validation rules belonging to a BindingGroup are run during the
        /// process of updating the source values of the bindings.  Each rule 
        /// indicates where in that process it should run.
        /// 
        public Collection ValidationRules
        { 
            get { return _validationRules; }
        } 
 
        /// 
        /// The collection of binding expressions belonging to this BindingGroup. 
        /// 
        public Collection BindingExpressions
        {
            get { return _bindingExpressions; } 
        }
 
        ///  
        /// The name of this BindingGroup.  A binding can elect to join this group
        /// by declaring its BindingGroupName to match the name of the group. 
        /// 
        public string Name
        {
            get { return _name; } 
            set { _name = value; }
        } 
 
        /// 
        /// When NotifyOnValidationError is set to True, the binding group will 
        /// raise a Validation.ValidationError event when its validation state changes.
        /// 
        public bool NotifyOnValidationError
        { 
            get { return _notifyOnValidationError; }
            set { _notifyOnValidationError = value; } 
        } 

        ///  
        /// CanRestoreValues returns True if the binding group can restore
        /// each of its sources (during ) to the state
        /// they had at the time of the most recent .
        /// This depends on whether the current sources provide a suitable 
        /// mechanism to implement the rollback, such as .
        ///  
        public bool CanRestoreValues 
        {
            get 
            {
                IList items = Items;
                for (int i=items.Count-1; i>=0; --i)
                { 
                    if (!(items[i] is IEditableObject))
                    { 
                        return false; 
                    }
                } 

                return true;
            }
        } 

        ///  
        /// The collection of items used as sources in the bindings owned by 
        /// this BindingGroup.  Each item appears only once, even if it is used
        /// by several bindings. 
        /// 
        /// 
        /// The Items property returns a snapshot collection, reflecting the state
        /// of the BindingGroup at the time of the call.  As bindings in the group 
        /// change to use different source items, the changes are not immediately
        /// visible in the collection.  They become visible only when the property is 
        /// queried again. 
        /// 
        public IList Items 
        {
            get
            {
                // rebuild the Items collection, if necessary 
                if (!_isItemsValid)
                { 
                    // find the new set of items 
                    IList newItems = _getValueTable.UniqueItems();
 
                    // modify the Items collection to match the new set
                    // First, remove items that no longer appear
                    for (int i=_itemsRW.Count-1;  i >= 0;  --i)
                    { 
                        int index = FindIndexOf(_itemsRW[i], newItems);
                        if (index >= 0) 
                        { 
                            newItems.RemoveAt(index);   // common item, don't add it later
                        } 
                        else
                        {
                            _itemsRW.RemoveAt(i);       // item no longer appears, remove it now
                        } 
                    }
 
                    // then add items that are really new 
                    for (int i=newItems.Count-1;  i>=0;  --i)
                    { 
                        _itemsRW.Add(newItems[i]);
                    }

                    _isItemsValid = true; 
                }
 
                return _items; 
            }
        } 

        #endregion Public properties

        #region Public Methods 

        //------------------------------------------------------ 
        // 
        //  Public Methods
        // 
        //------------------------------------------------------

        /// 
        /// Begin an editing transaction.  For each source that supports it, 
        /// the binding group asks the source to save its state, for possible
        /// restoration during . 
        ///  
        public void BeginEdit()
        { 
            IList items = Items;
            for (int i=items.Count-1; i>=0; --i)
            {
                IEditableObject ieo = items[i] as IEditableObject; 
                if (ieo != null)
                { 
                    ieo.BeginEdit(); 
                }
            } 
        }

        /// 
        /// End an editing transaction.  The binding group attempts to update all 
        /// its sources with the proposed new values held in the target UI elements.
        /// All validation rules are run, at the times requested by the rules. 
        ///  
        /// 
        /// True, if all validation rules succeed and no errors arise. 
        /// False, otherwise.
        /// 
        public bool CommitEdit()
        { 
            return UpdateAndValidate(ValidationStep.CommittedValue);
        } 
 
        /// 
        /// Cancel an editing transaction.  For each source that supports it, 
        /// the binding group asks the source to restore itself to the state saved
        /// at the most recent .  Then the binding group
        /// updates all targets with values from their respective sources, discarding
        /// any "dirty" values held in the targets. 
        /// 
        public void CancelEdit() 
        { 
            // restore values
            IList items = Items; 
            for (int i=items.Count-1; i>=0; --i)
            {
                IEditableObject ieo = items[i] as IEditableObject;
                if (ieo != null) 
                {
                    ieo.CancelEdit(); 
                } 
            }
 
            // update targets
            for (int i=_bindingExpressions.Count - 1; i>=0; --i)
            {
                _bindingExpressions[i].UpdateTarget(); 
            }
        } 
 
        /// 
        /// Run the validation process up to the ConvertedProposedValue step. 
        /// This runs all validation rules marked as RawProposedValue or
        /// ConvertedProposedValue, but does not update any sources with new values.
        /// 
        ///  
        /// True, if all validation rules succeed and no errors arise.
        /// False, otherwise. 
        ///  
        public bool ValidateWithoutUpdate()
        { 
            return UpdateAndValidate(ValidationStep.ConvertedProposedValue);
        }

        ///  
        /// Run the validation process up to the UpdatedValue step.
        /// This runs all validation rules marked as RawProposedValue or 
        /// ConvertedProposedValue, updates the sources with new values, and 
        /// runs rules marked as Updatedvalue.
        ///  
        /// 
        /// True, if all validation rules succeed and no errors arise.
        /// False, otherwise.
        ///  
        public bool UpdateSources()
        { 
            return UpdateAndValidate(ValidationStep.UpdatedValue); 
        }
 
        /// 
        /// Find the binding that uses the given item and property, and return
        /// the value appropriate to the current validation step.
        ///  
        /// 
        /// the binding group does not contain a binding corresponding to the 
        /// given item and property. 
        /// 
        ///  
        /// the value is not available.  This could be because an earlier validation
        /// rule deemed the value invalid, or because the value could not be produced
        /// for some reason, such as conversion failure.
        ///  
        /// 
        /// This method is intended to be called from a validation rule, during 
        /// its Validate method. 
        /// 
        public object GetValue(object item, string propertyName) 
        {
            object value;

            if (TryGetValueImpl(item, propertyName, out value)) 
            {
                return value; 
            } 

            if (value == Binding.DoNothing) 
                throw new ValueUnavailableException(SR.Get(SRID.BindingGroup_NoEntry, item, propertyName));
            else
                throw new ValueUnavailableException(SR.Get(SRID.BindingGroup_ValueUnavailable, item, propertyName));
        } 

        ///  
        /// Find the binding that uses the given item and property, and return 
        /// the value appropriate to the current validation step.
        ///  
        /// 
        /// The method normally returns true and sets 'value' to the requested value.
        /// If the value is not available, the method returns false and sets 'value'
        /// to DependencyProperty.UnsetValue. 
        /// 
        ///  
        /// This method is intended to be called from a validation rule, during 
        /// its Validate method.
        ///  
        public bool TryGetValue(object item, string propertyName, out object value)
        {
            bool result = TryGetValueImpl(item, propertyName, out value);
 
            // TryGetValueImpl sets value to DoNothing to signal "no entry".
            // TryGetValue should treat this as just another unavailable value. 
            if (value == Binding.DoNothing) 
            {
                value = DependencyProperty.UnsetValue; 
            }

            return result;
        } 

        bool TryGetValueImpl(object item, string propertyName, out object value) 
        { 
            GetValueTableEntry entry = _getValueTable[item, propertyName];
            if (entry == null) 
            {
                value = Binding.DoNothing;   // signal "no entry"
                return false;
            } 

            switch (_validationStep) 
            { 
                case ValidationStep.RawProposedValue:
                case ValidationStep.ConvertedProposedValue: 
                case ValidationStep.UpdatedValue:
                case ValidationStep.CommittedValue:
                    value = entry.Value;
                    break; 

                // outside of validation process, use the raw value 
                default: 
                    value = entry.BindingExpressionBase.RootBindingExpression.GetRawProposedValue();
                    break; 
            }

            if (value == Binding.DoNothing)
            { 
                // a converter has indicated that no value should be written to the source object.
                // Therefore the source's value is the one to return to the validation rule. 
                BindingExpression bindingExpression = (BindingExpression)entry.BindingExpressionBase; 
                value = bindingExpression.SourceValue;
            } 

            return (value != DependencyProperty.UnsetValue);
        }
 
        #endregion Public Methods
 
        #region Internal properties 

        //----------------------------------------------------- 
        //
        //  Internal properties
        //
        //------------------------------------------------------ 

        // Define the DO's inheritance context 
        internal override DependencyObject InheritanceContext 
        {
            get { return _inheritanceContext; } 
        }

        // Receive a new inheritance context (this will be a FE/FCE)
        internal override void AddInheritanceContext(DependencyObject context, DependencyProperty property) 
        {
            if (property != null && property.PropertyType != typeof(BindingGroup) && 
                TraceData.IsEnabled) 
            {
                string name = (property != null) ? property.Name : "(null)"; 
                TraceData.Trace(TraceEventType.Warning,
                        TraceData.BindingGroupWrongProperty(name, context.GetType().FullName));
            }
 
            InheritanceContextHelper.AddInheritanceContext(context,
                                                              this, 
                                                              ref _hasMultipleInheritanceContexts, 
                                                              ref _inheritanceContext );
 
            // sharing a BindingGroup among multiple hosts is bad - we wouldn't know which host
            // to send the errors to (just for starters).  But sharing an ItemBindingGroup is
            // expected - this is what happens normally in a hierarchical control like TreeView.
            // The following code tries to detect the bad case and warn the user that something 
            // is amiss.
            if (_hasMultipleInheritanceContexts && property != ItemsControl.ItemBindingGroupProperty && TraceData.IsEnabled) 
            { 
                TraceData.Trace(TraceEventType.Warning,
                        TraceData.BindingGroupMultipleInheritance); 
            }
        }

        // Remove an inheritance context (this will be a FE/FCE) 
        internal override void RemoveInheritanceContext(DependencyObject context, DependencyProperty property)
        { 
            InheritanceContextHelper.RemoveInheritanceContext(context, 
                                                                  this,
                                                                  ref _hasMultipleInheritanceContexts, 
                                                                  ref _inheritanceContext);
        }

        // Says if the current instance has multiple InheritanceContexts 
        internal override bool HasMultipleInheritanceContexts
        { 
            get { return _hasMultipleInheritanceContexts; } 
        }
 
        #endregion Internal properties

        #region Internal methods
 
        //-----------------------------------------------------
        // 
        //  Internal methods 
        //
        //----------------------------------------------------- 

        // called when a leaf binding changes its source item
        internal void UpdateTable(BindingExpression bindingExpression)
        { 
            _getValueTable.Update(bindingExpression);
            _isItemsValid = false; 
        } 

        // add an entry to the value table for the given binding 
        internal void AddToValueTable(BindingExpressionBase bindingExpressionBase)
        {
            _getValueTable.EnsureEntry(bindingExpressionBase);
        } 

        // get the value for the given binding 
        internal object GetValue(BindingExpressionBase bindingExpressionBase) 
        {
            return _getValueTable.GetValue(bindingExpressionBase); 
        }

        // set the value for the given binding
        internal void SetValue(BindingExpressionBase bindingExpressionBase, object value) 
        {
            _getValueTable.SetValue(bindingExpressionBase, value); 
        } 

        // set values to "source" for all bindings under the given root 
        internal void UseSourceValue(BindingExpressionBase bindingExpressionBase)
        {
            _getValueTable.UseSourceValue(bindingExpressionBase);
        } 

        // add a validation error to the mentor's list 
        internal void AddValidationError(ValidationError validationError) 
        {
            DependencyObject mentor = Helper.FindMentor(this); 
            if (mentor == null)
                return;

            Validation.AddValidationError(validationError, mentor, NotifyOnValidationError); 
        }
 
        // remove a validation error from the mentor's list 
        internal void RemoveValidationError(ValidationError validationError)
        { 
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor == null)
                return;
 
            Validation.RemoveValidationError(validationError, mentor, NotifyOnValidationError);
        } 
 
        // remove all errors raised at the given step, in preparation for running
        // the rules at that step 
        void ClearValidationErrors(ValidationStep validationStep)
        {
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor == null) 
                return;
 
            ValidationErrorCollection validationErrors = Validation.GetErrorsInternal(mentor); 
            if (validationErrors == null)
                return; 

            for (int i=validationErrors.Count-1; i>=0; --i)
            {
                ValidationError validationError = validationErrors[i]; 
                if (validationError.BindingInError == this &&
                    validationError.RuleInError.ValidationStep == validationStep) 
                { 
                    RemoveValidationError(validationError);
                } 
            }
        }

        #endregion Internal methods 

        #region Private methods 
 
        //-----------------------------------------------------
        // 
        //  Private methods
        //
        //------------------------------------------------------
 
        // run the validation process up to the indicated step
        bool UpdateAndValidate(ValidationStep validationStep) 
        { 
            bool result = true;
 
            for (_validationStep = ValidationStep.RawProposedValue;
                    _validationStep <= validationStep;
                    ++ _validationStep)
            { 
                switch (_validationStep)
                { 
                    case ValidationStep.RawProposedValue: 
                        _getValueTable.ResetValues();
                        break; 
                    case ValidationStep.ConvertedProposedValue:
                        ObtainConvertedProposedValues();
                        break;
                    case ValidationStep.UpdatedValue: 
                        UpdateValues();
                        break; 
                    case ValidationStep.CommittedValue: 
                        CommitValues();
                        break; 
                }

                if (!CheckValidationRules())
                { 
                    result = false;
                    break; 
                } 
            }
 
            _validationStep = (ValidationStep)(-1);
            _getValueTable.ResetValues();

            return result; 
        }
 
        // apply conversions to each binding in the group 
        void ObtainConvertedProposedValues()
        { 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            {
                _bindingExpressions[i].ObtainConvertedProposedValue(this);
            } 
        }
 
        // update the source value of each binding in the group 
        void UpdateValues()
        { 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            {
                _bindingExpressions[i].UpdateSource(this);
            } 
        }
 
        // check the validation rules for the current step 
        bool CheckValidationRules()
        { 
            bool result = true;

            // clear old errors arising from this step
            ClearValidationErrors(_validationStep); 

            // check rules attached to the bindings 
            for (int i=_bindingExpressions.Count-1; i>=0; --i) 
            {
                if (!_bindingExpressions[i].CheckValidationRules(this, _validationStep)) 
                {
                    result = false;
                }
            } 

            // check rules attached to the binding group 
            CultureInfo culture = GetCulture(); 
            for (int i=0, n=_validationRules.Count; i=0; --i)
            {
                IEditableObject ieo = items[i] as IEditableObject;
                if (ieo != null) 
                {
                    ieo.EndEdit(); 
                } 
            }
        } 

        // find the index of an item in a list, where both the item and
        // the list use WeakReferences
        static int FindIndexOf(WeakReference wr, IList list) 
        {
            object item = wr.Target; 
            if (item == null) 
                return -1;
 
            for (int i=0, n=list.Count; i list = _getValueTable.RemoveRootBinding(root); 

            // tell each expression it is leaving the group 
            foreach (BindingExpressionBase expr in list)
            {
                expr.OnBindingGroupChanged(/*joining*/ false);
 
                // also remove the expression from our collection.  Normally this is
                // a no-op, as we only get here after the expression has been removed, 
                // and implicit membership only adds root expressions to the collection. 
                // But an app (through confusion or malice) could explicitly add two
                // or more expressions with the same root.  We handle that case here. 
                _bindingExpressions.Remove(expr);
            }

            // cut the root's link to the group 
            root.LeaveBindingGroup();
        } 
 
        // remove all binding expressions from the group
        void RemoveAllBindingExpressions() 
        {
            // we can't use the BindingExpressions collection - it has already
            // been cleared.  Instead, find the expressions that need work by
            // looking in the GetValue table. 
            GetValueTableEntry entry;
            while ((entry = _getValueTable.GetFirstEntry()) != null) 
            { 
                RemoveBindingExpression(entry.BindingExpressionBase);
            } 
        }

        #endregion Private methods
 
        #region Private data
 
        //----------------------------------------------------- 
        //
        //  Private data 
        //
        //------------------------------------------------------

        ValidationRuleCollection    _validationRules; 
        string                      _name;
        bool                        _notifyOnValidationError; 
 
        BindingExpressionCollection _bindingExpressions;
        bool                        _isItemsValid; 
        ValidationStep              _validationStep = (ValidationStep)(-1);
        GetValueTable               _getValueTable = new GetValueTable();
        Collection   _itemsRW;
        WeakReadOnlyCollection _items; 
        CultureInfo                 _culture;
 
        internal static readonly object DeferredTargetValue = new NamedObject("DeferredTargetValue"); 
        internal static readonly object DeferredSourceValue = new NamedObject("DeferredSourceValue");
 
        // Fields to implement DO's inheritance context
        DependencyObject    _inheritanceContext;
        bool                _hasMultipleInheritanceContexts;
 
        #endregion Private data
 
        #region Private types 

        //------------------------------------------------------ 
        //
        //  Private types
        //
        //----------------------------------------------------- 

        // to support GetValue, we maintain an associative array of all the bindings, 
        // items, and property names that affect a binding group. 
        private class GetValueTable
        { 
            // lookup by item and propertyName
            public GetValueTableEntry this[object item, string propertyName]
            {
                get 
                {
                    for (int i=_table.Count-1; i >= 0; --i) 
                    { 
                        GetValueTableEntry entry = _table[i];
                        if (propertyName == entry.PropertyName && 
                            Object.Equals(item, entry.Item))
                        {
                            return entry;
                        } 
                    }
 
                    return null; 
                }
            } 

            // lookup by binding
            public GetValueTableEntry this[BindingExpressionBase bindingExpressionBase]
            { 
                get
                { 
                    for (int i=_table.Count-1; i >= 0; --i) 
                    {
                        GetValueTableEntry entry = _table[i]; 
                        if (bindingExpressionBase == entry.BindingExpressionBase)
                        {
                            return entry;
                        } 
                    }
 
                    return null; 
                }
            } 

            // ensure an entry for the given binding
            public void EnsureEntry(BindingExpressionBase bindingExpressionBase)
            { 
                GetValueTableEntry entry = this[bindingExpressionBase];
                if (entry == null) 
                { 
                    _table.Add(new GetValueTableEntry(bindingExpressionBase));
                } 
            }

            // update (or add) the entry for the given leaf binding
            public void Update(BindingExpression bindingExpression) 
            {
                GetValueTableEntry entry = this[bindingExpression]; 
                if (entry == null) 
                {
                    _table.Add(new GetValueTableEntry(bindingExpression)); 
                }
                else
                {
                    entry.Update(bindingExpression); 
                }
            } 
 
            // remove all the entries for the given root binding.  Return the list of expressions.
            public List RemoveRootBinding(BindingExpressionBase rootBindingExpression) 
            {
                List result = new List();

                for (int i=_table.Count-1; i >= 0; --i) 
                {
                    BindingExpressionBase expr = _table[i].BindingExpressionBase; 
                    if (expr.RootBindingExpression == rootBindingExpression) 
                    {
                        result.Add(expr); 
                        _table.RemoveAt(i);
                    }
                }
 
                return result;
            } 
 
            // return a list of the unique items (wrapped in WeakReferences)
            public IList UniqueItems() 
            {
                List list = new List();

                for (int i=_table.Count-1; i >= 0; --i) 
                {
                    WeakReference itemWR = _table[i].ItemReference; 
                    if (itemWR != null && BindingGroup.FindIndexOf(itemWR, list) < 0) 
                    {
                        list.Add(itemWR); 
                    }
                }

                return list; 
            }
 
            // get the value for a binding expression 
            public object GetValue(BindingExpressionBase bindingExpressionBase)
            { 
                GetValueTableEntry entry = this[bindingExpressionBase];
                return (entry != null) ? entry.Value : DependencyProperty.UnsetValue;
            }
 
            // set the value for a binding expression
            public void SetValue(BindingExpressionBase bindingExpressionBase, object value) 
            { 
                GetValueTableEntry entry = this[bindingExpressionBase];
                if (entry != null) 
                {
                    entry.Value = value;
                }
            } 

            // reset values to "raw" 
            public void ResetValues() 
            {
                for (int i=_table.Count-1; i>=0; --i) 
                {
                    _table[i].Value = BindingGroup.DeferredTargetValue;
                }
            } 

            // set values to "source" for all bindings under the given root 
            public void UseSourceValue(BindingExpressionBase rootBindingExpression) 
            {
                for (int i=_table.Count-1; i>=0; --i) 
                {
                    if (_table[i].BindingExpressionBase.RootBindingExpression == rootBindingExpression)
                    {
                        _table[i].Value = BindingGroup.DeferredSourceValue; 
                    }
                } 
            } 

            // return the first entry in the table (or null) 
            public GetValueTableEntry GetFirstEntry()
            {
                return (_table.Count > 0) ? _table[0] : null;
            } 

            Collection _table = new Collection(); 
        } 

        // a single entry in the GetValueTable 
        private class GetValueTableEntry
        {
            public GetValueTableEntry(BindingExpressionBase bindingExpressionBase)
            { 
                _bindingExpressionBase = bindingExpressionBase;
            } 
 
            public void Update(BindingExpression bindingExpression)
            { 
                if (_itemWR == null)
                {
                    _itemWR = new WeakReference(bindingExpression.SourceItem);  // WR to avoid leaks
                } 
                else
                { 
                    _itemWR.Target = bindingExpression.SourceItem; 
                }
 
                _propertyName = bindingExpression.SourcePropertyName;
            }

            public object Item 
            {
                get { return _itemWR.Target; } 
            } 

            public WeakReference ItemReference 
            {
                get { return _itemWR; }
            }
 
            public string PropertyName
            { 
                get { return _propertyName; } 
            }
 
            public BindingExpressionBase BindingExpressionBase
            {
                get { return _bindingExpressionBase; }
            } 

            public object Value 
            { 
                get
                { 
                    if (_value == BindingGroup.DeferredTargetValue)
                    {
                        _value = _bindingExpressionBase.RootBindingExpression.GetRawProposedValue();
                    } 
                    else if (_value == BindingGroup.DeferredSourceValue)
                    { 
                        BindingExpression bindingExpression = _bindingExpressionBase as BindingExpression; 
                        Debug.Assert(bindingExpression != null, "do not ask for source value from a [Multi,Priority]Binding");
                        _value = (bindingExpression != null) ? bindingExpression.SourceValue : DependencyProperty.UnsetValue; 
                    }

                    return _value;
                } 
                set { _value = value; }
            } 
 
            BindingExpressionBase   _bindingExpressionBase;
            WeakReference   _itemWR; 
            string          _propertyName;
            object          _value = BindingGroup.DeferredTargetValue;
        }
 
        // add some error-checking to ObservableCollection
        class BindingExpressionCollection : ObservableCollection 
        { 
            /// 
            /// Called by base class Collection<T> when an item is added to list; 
            /// raises a CollectionChanged event to any listeners.
            /// 
            protected override void InsertItem(int index, BindingExpressionBase item)
            { 
                if (item == null)
                { 
                    throw new ArgumentNullException("item"); 
                }
 
                base.InsertItem(index, item);
            }

            ///  
            /// Called by base class Collection<T> when an item is set in list;
            /// raises a CollectionChanged event to any listeners. 
            ///  
            protected override void SetItem(int index, BindingExpressionBase item)
            { 
                if (item == null)
                {
                    throw new ArgumentNullException("item");
                } 

                base.SetItem(index, item); 
            } 
        }
 
        #endregion Private types
    }
}

// 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 BindingGroup object, manages a collection of bindings. 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Collections;               // IList
using System.Collections.Generic;       // IList
using System.Collections.ObjectModel;   // Collection 
using System.Collections.Specialized;   // INotifyCollectionChanged
using System.ComponentModel;            // IEditableObject 
using System.Diagnostics;               // Debug 
using System.Globalization;             // CultureInfo
 
using System.Windows;
using System.Windows.Controls;          // ValidationRule
using MS.Internal.Controls;             // ValidationRuleCollection
using MS.Internal;                      // InheritanceContextHelper 

namespace System.Windows.Data 
{ 
    /// 
    /// A BindingGroup manages a collection of bindings, and provides services for 
    /// item-level and cross-binding validation.
    /// 
    public class BindingGroup : DependencyObject
    { 
        #region Constructors
 
        //----------------------------------------------------- 
        //
        //  Constructors 
        //
        //-----------------------------------------------------

        ///  
        ///     Initializes a new instance of the BindingGroup class.
        ///  
        public BindingGroup() 
        {
            _validationRules = new ValidationRuleCollection(); 
            Initialize();
        }

        // clone the binding group.  Called when setting a binding group on a 
        // container, from the ItemControl's ItemBindingGroup.
        internal BindingGroup(BindingGroup master) 
        { 
            _validationRules = master._validationRules;
            _name = master._name; 
            _notifyOnValidationError = master._notifyOnValidationError;
            Initialize();
        }
 
        void Initialize()
        { 
            _bindingExpressions = new BindingExpressionCollection(); 
            ((INotifyCollectionChanged)_bindingExpressions).CollectionChanged += new NotifyCollectionChangedEventHandler(OnBindingsChanged);
 
            _itemsRW = new Collection();
            _items = new WeakReadOnlyCollection(_itemsRW);
        }
 

        #endregion Constructors 
 
        #region Public properties
 
        //------------------------------------------------------
        //
        //  Public properties
        // 
        //-----------------------------------------------------
 
        ///  
        /// The validation rules belonging to a BindingGroup are run during the
        /// process of updating the source values of the bindings.  Each rule 
        /// indicates where in that process it should run.
        /// 
        public Collection ValidationRules
        { 
            get { return _validationRules; }
        } 
 
        /// 
        /// The collection of binding expressions belonging to this BindingGroup. 
        /// 
        public Collection BindingExpressions
        {
            get { return _bindingExpressions; } 
        }
 
        ///  
        /// The name of this BindingGroup.  A binding can elect to join this group
        /// by declaring its BindingGroupName to match the name of the group. 
        /// 
        public string Name
        {
            get { return _name; } 
            set { _name = value; }
        } 
 
        /// 
        /// When NotifyOnValidationError is set to True, the binding group will 
        /// raise a Validation.ValidationError event when its validation state changes.
        /// 
        public bool NotifyOnValidationError
        { 
            get { return _notifyOnValidationError; }
            set { _notifyOnValidationError = value; } 
        } 

        ///  
        /// CanRestoreValues returns True if the binding group can restore
        /// each of its sources (during ) to the state
        /// they had at the time of the most recent .
        /// This depends on whether the current sources provide a suitable 
        /// mechanism to implement the rollback, such as .
        ///  
        public bool CanRestoreValues 
        {
            get 
            {
                IList items = Items;
                for (int i=items.Count-1; i>=0; --i)
                { 
                    if (!(items[i] is IEditableObject))
                    { 
                        return false; 
                    }
                } 

                return true;
            }
        } 

        ///  
        /// The collection of items used as sources in the bindings owned by 
        /// this BindingGroup.  Each item appears only once, even if it is used
        /// by several bindings. 
        /// 
        /// 
        /// The Items property returns a snapshot collection, reflecting the state
        /// of the BindingGroup at the time of the call.  As bindings in the group 
        /// change to use different source items, the changes are not immediately
        /// visible in the collection.  They become visible only when the property is 
        /// queried again. 
        /// 
        public IList Items 
        {
            get
            {
                // rebuild the Items collection, if necessary 
                if (!_isItemsValid)
                { 
                    // find the new set of items 
                    IList newItems = _getValueTable.UniqueItems();
 
                    // modify the Items collection to match the new set
                    // First, remove items that no longer appear
                    for (int i=_itemsRW.Count-1;  i >= 0;  --i)
                    { 
                        int index = FindIndexOf(_itemsRW[i], newItems);
                        if (index >= 0) 
                        { 
                            newItems.RemoveAt(index);   // common item, don't add it later
                        } 
                        else
                        {
                            _itemsRW.RemoveAt(i);       // item no longer appears, remove it now
                        } 
                    }
 
                    // then add items that are really new 
                    for (int i=newItems.Count-1;  i>=0;  --i)
                    { 
                        _itemsRW.Add(newItems[i]);
                    }

                    _isItemsValid = true; 
                }
 
                return _items; 
            }
        } 

        #endregion Public properties

        #region Public Methods 

        //------------------------------------------------------ 
        // 
        //  Public Methods
        // 
        //------------------------------------------------------

        /// 
        /// Begin an editing transaction.  For each source that supports it, 
        /// the binding group asks the source to save its state, for possible
        /// restoration during . 
        ///  
        public void BeginEdit()
        { 
            IList items = Items;
            for (int i=items.Count-1; i>=0; --i)
            {
                IEditableObject ieo = items[i] as IEditableObject; 
                if (ieo != null)
                { 
                    ieo.BeginEdit(); 
                }
            } 
        }

        /// 
        /// End an editing transaction.  The binding group attempts to update all 
        /// its sources with the proposed new values held in the target UI elements.
        /// All validation rules are run, at the times requested by the rules. 
        ///  
        /// 
        /// True, if all validation rules succeed and no errors arise. 
        /// False, otherwise.
        /// 
        public bool CommitEdit()
        { 
            return UpdateAndValidate(ValidationStep.CommittedValue);
        } 
 
        /// 
        /// Cancel an editing transaction.  For each source that supports it, 
        /// the binding group asks the source to restore itself to the state saved
        /// at the most recent .  Then the binding group
        /// updates all targets with values from their respective sources, discarding
        /// any "dirty" values held in the targets. 
        /// 
        public void CancelEdit() 
        { 
            // restore values
            IList items = Items; 
            for (int i=items.Count-1; i>=0; --i)
            {
                IEditableObject ieo = items[i] as IEditableObject;
                if (ieo != null) 
                {
                    ieo.CancelEdit(); 
                } 
            }
 
            // update targets
            for (int i=_bindingExpressions.Count - 1; i>=0; --i)
            {
                _bindingExpressions[i].UpdateTarget(); 
            }
        } 
 
        /// 
        /// Run the validation process up to the ConvertedProposedValue step. 
        /// This runs all validation rules marked as RawProposedValue or
        /// ConvertedProposedValue, but does not update any sources with new values.
        /// 
        ///  
        /// True, if all validation rules succeed and no errors arise.
        /// False, otherwise. 
        ///  
        public bool ValidateWithoutUpdate()
        { 
            return UpdateAndValidate(ValidationStep.ConvertedProposedValue);
        }

        ///  
        /// Run the validation process up to the UpdatedValue step.
        /// This runs all validation rules marked as RawProposedValue or 
        /// ConvertedProposedValue, updates the sources with new values, and 
        /// runs rules marked as Updatedvalue.
        ///  
        /// 
        /// True, if all validation rules succeed and no errors arise.
        /// False, otherwise.
        ///  
        public bool UpdateSources()
        { 
            return UpdateAndValidate(ValidationStep.UpdatedValue); 
        }
 
        /// 
        /// Find the binding that uses the given item and property, and return
        /// the value appropriate to the current validation step.
        ///  
        /// 
        /// the binding group does not contain a binding corresponding to the 
        /// given item and property. 
        /// 
        ///  
        /// the value is not available.  This could be because an earlier validation
        /// rule deemed the value invalid, or because the value could not be produced
        /// for some reason, such as conversion failure.
        ///  
        /// 
        /// This method is intended to be called from a validation rule, during 
        /// its Validate method. 
        /// 
        public object GetValue(object item, string propertyName) 
        {
            object value;

            if (TryGetValueImpl(item, propertyName, out value)) 
            {
                return value; 
            } 

            if (value == Binding.DoNothing) 
                throw new ValueUnavailableException(SR.Get(SRID.BindingGroup_NoEntry, item, propertyName));
            else
                throw new ValueUnavailableException(SR.Get(SRID.BindingGroup_ValueUnavailable, item, propertyName));
        } 

        ///  
        /// Find the binding that uses the given item and property, and return 
        /// the value appropriate to the current validation step.
        ///  
        /// 
        /// The method normally returns true and sets 'value' to the requested value.
        /// If the value is not available, the method returns false and sets 'value'
        /// to DependencyProperty.UnsetValue. 
        /// 
        ///  
        /// This method is intended to be called from a validation rule, during 
        /// its Validate method.
        ///  
        public bool TryGetValue(object item, string propertyName, out object value)
        {
            bool result = TryGetValueImpl(item, propertyName, out value);
 
            // TryGetValueImpl sets value to DoNothing to signal "no entry".
            // TryGetValue should treat this as just another unavailable value. 
            if (value == Binding.DoNothing) 
            {
                value = DependencyProperty.UnsetValue; 
            }

            return result;
        } 

        bool TryGetValueImpl(object item, string propertyName, out object value) 
        { 
            GetValueTableEntry entry = _getValueTable[item, propertyName];
            if (entry == null) 
            {
                value = Binding.DoNothing;   // signal "no entry"
                return false;
            } 

            switch (_validationStep) 
            { 
                case ValidationStep.RawProposedValue:
                case ValidationStep.ConvertedProposedValue: 
                case ValidationStep.UpdatedValue:
                case ValidationStep.CommittedValue:
                    value = entry.Value;
                    break; 

                // outside of validation process, use the raw value 
                default: 
                    value = entry.BindingExpressionBase.RootBindingExpression.GetRawProposedValue();
                    break; 
            }

            if (value == Binding.DoNothing)
            { 
                // a converter has indicated that no value should be written to the source object.
                // Therefore the source's value is the one to return to the validation rule. 
                BindingExpression bindingExpression = (BindingExpression)entry.BindingExpressionBase; 
                value = bindingExpression.SourceValue;
            } 

            return (value != DependencyProperty.UnsetValue);
        }
 
        #endregion Public Methods
 
        #region Internal properties 

        //----------------------------------------------------- 
        //
        //  Internal properties
        //
        //------------------------------------------------------ 

        // Define the DO's inheritance context 
        internal override DependencyObject InheritanceContext 
        {
            get { return _inheritanceContext; } 
        }

        // Receive a new inheritance context (this will be a FE/FCE)
        internal override void AddInheritanceContext(DependencyObject context, DependencyProperty property) 
        {
            if (property != null && property.PropertyType != typeof(BindingGroup) && 
                TraceData.IsEnabled) 
            {
                string name = (property != null) ? property.Name : "(null)"; 
                TraceData.Trace(TraceEventType.Warning,
                        TraceData.BindingGroupWrongProperty(name, context.GetType().FullName));
            }
 
            InheritanceContextHelper.AddInheritanceContext(context,
                                                              this, 
                                                              ref _hasMultipleInheritanceContexts, 
                                                              ref _inheritanceContext );
 
            // sharing a BindingGroup among multiple hosts is bad - we wouldn't know which host
            // to send the errors to (just for starters).  But sharing an ItemBindingGroup is
            // expected - this is what happens normally in a hierarchical control like TreeView.
            // The following code tries to detect the bad case and warn the user that something 
            // is amiss.
            if (_hasMultipleInheritanceContexts && property != ItemsControl.ItemBindingGroupProperty && TraceData.IsEnabled) 
            { 
                TraceData.Trace(TraceEventType.Warning,
                        TraceData.BindingGroupMultipleInheritance); 
            }
        }

        // Remove an inheritance context (this will be a FE/FCE) 
        internal override void RemoveInheritanceContext(DependencyObject context, DependencyProperty property)
        { 
            InheritanceContextHelper.RemoveInheritanceContext(context, 
                                                                  this,
                                                                  ref _hasMultipleInheritanceContexts, 
                                                                  ref _inheritanceContext);
        }

        // Says if the current instance has multiple InheritanceContexts 
        internal override bool HasMultipleInheritanceContexts
        { 
            get { return _hasMultipleInheritanceContexts; } 
        }
 
        #endregion Internal properties

        #region Internal methods
 
        //-----------------------------------------------------
        // 
        //  Internal methods 
        //
        //----------------------------------------------------- 

        // called when a leaf binding changes its source item
        internal void UpdateTable(BindingExpression bindingExpression)
        { 
            _getValueTable.Update(bindingExpression);
            _isItemsValid = false; 
        } 

        // add an entry to the value table for the given binding 
        internal void AddToValueTable(BindingExpressionBase bindingExpressionBase)
        {
            _getValueTable.EnsureEntry(bindingExpressionBase);
        } 

        // get the value for the given binding 
        internal object GetValue(BindingExpressionBase bindingExpressionBase) 
        {
            return _getValueTable.GetValue(bindingExpressionBase); 
        }

        // set the value for the given binding
        internal void SetValue(BindingExpressionBase bindingExpressionBase, object value) 
        {
            _getValueTable.SetValue(bindingExpressionBase, value); 
        } 

        // set values to "source" for all bindings under the given root 
        internal void UseSourceValue(BindingExpressionBase bindingExpressionBase)
        {
            _getValueTable.UseSourceValue(bindingExpressionBase);
        } 

        // add a validation error to the mentor's list 
        internal void AddValidationError(ValidationError validationError) 
        {
            DependencyObject mentor = Helper.FindMentor(this); 
            if (mentor == null)
                return;

            Validation.AddValidationError(validationError, mentor, NotifyOnValidationError); 
        }
 
        // remove a validation error from the mentor's list 
        internal void RemoveValidationError(ValidationError validationError)
        { 
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor == null)
                return;
 
            Validation.RemoveValidationError(validationError, mentor, NotifyOnValidationError);
        } 
 
        // remove all errors raised at the given step, in preparation for running
        // the rules at that step 
        void ClearValidationErrors(ValidationStep validationStep)
        {
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor == null) 
                return;
 
            ValidationErrorCollection validationErrors = Validation.GetErrorsInternal(mentor); 
            if (validationErrors == null)
                return; 

            for (int i=validationErrors.Count-1; i>=0; --i)
            {
                ValidationError validationError = validationErrors[i]; 
                if (validationError.BindingInError == this &&
                    validationError.RuleInError.ValidationStep == validationStep) 
                { 
                    RemoveValidationError(validationError);
                } 
            }
        }

        #endregion Internal methods 

        #region Private methods 
 
        //-----------------------------------------------------
        // 
        //  Private methods
        //
        //------------------------------------------------------
 
        // run the validation process up to the indicated step
        bool UpdateAndValidate(ValidationStep validationStep) 
        { 
            bool result = true;
 
            for (_validationStep = ValidationStep.RawProposedValue;
                    _validationStep <= validationStep;
                    ++ _validationStep)
            { 
                switch (_validationStep)
                { 
                    case ValidationStep.RawProposedValue: 
                        _getValueTable.ResetValues();
                        break; 
                    case ValidationStep.ConvertedProposedValue:
                        ObtainConvertedProposedValues();
                        break;
                    case ValidationStep.UpdatedValue: 
                        UpdateValues();
                        break; 
                    case ValidationStep.CommittedValue: 
                        CommitValues();
                        break; 
                }

                if (!CheckValidationRules())
                { 
                    result = false;
                    break; 
                } 
            }
 
            _validationStep = (ValidationStep)(-1);
            _getValueTable.ResetValues();

            return result; 
        }
 
        // apply conversions to each binding in the group 
        void ObtainConvertedProposedValues()
        { 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            {
                _bindingExpressions[i].ObtainConvertedProposedValue(this);
            } 
        }
 
        // update the source value of each binding in the group 
        void UpdateValues()
        { 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            {
                _bindingExpressions[i].UpdateSource(this);
            } 
        }
 
        // check the validation rules for the current step 
        bool CheckValidationRules()
        { 
            bool result = true;

            // clear old errors arising from this step
            ClearValidationErrors(_validationStep); 

            // check rules attached to the bindings 
            for (int i=_bindingExpressions.Count-1; i>=0; --i) 
            {
                if (!_bindingExpressions[i].CheckValidationRules(this, _validationStep)) 
                {
                    result = false;
                }
            } 

            // check rules attached to the binding group 
            CultureInfo culture = GetCulture(); 
            for (int i=0, n=_validationRules.Count; i=0; --i)
            {
                IEditableObject ieo = items[i] as IEditableObject;
                if (ieo != null) 
                {
                    ieo.EndEdit(); 
                } 
            }
        } 

        // find the index of an item in a list, where both the item and
        // the list use WeakReferences
        static int FindIndexOf(WeakReference wr, IList list) 
        {
            object item = wr.Target; 
            if (item == null) 
                return -1;
 
            for (int i=0, n=list.Count; i list = _getValueTable.RemoveRootBinding(root); 

            // tell each expression it is leaving the group 
            foreach (BindingExpressionBase expr in list)
            {
                expr.OnBindingGroupChanged(/*joining*/ false);
 
                // also remove the expression from our collection.  Normally this is
                // a no-op, as we only get here after the expression has been removed, 
                // and implicit membership only adds root expressions to the collection. 
                // But an app (through confusion or malice) could explicitly add two
                // or more expressions with the same root.  We handle that case here. 
                _bindingExpressions.Remove(expr);
            }

            // cut the root's link to the group 
            root.LeaveBindingGroup();
        } 
 
        // remove all binding expressions from the group
        void RemoveAllBindingExpressions() 
        {
            // we can't use the BindingExpressions collection - it has already
            // been cleared.  Instead, find the expressions that need work by
            // looking in the GetValue table. 
            GetValueTableEntry entry;
            while ((entry = _getValueTable.GetFirstEntry()) != null) 
            { 
                RemoveBindingExpression(entry.BindingExpressionBase);
            } 
        }

        #endregion Private methods
 
        #region Private data
 
        //----------------------------------------------------- 
        //
        //  Private data 
        //
        //------------------------------------------------------

        ValidationRuleCollection    _validationRules; 
        string                      _name;
        bool                        _notifyOnValidationError; 
 
        BindingExpressionCollection _bindingExpressions;
        bool                        _isItemsValid; 
        ValidationStep              _validationStep = (ValidationStep)(-1);
        GetValueTable               _getValueTable = new GetValueTable();
        Collection   _itemsRW;
        WeakReadOnlyCollection _items; 
        CultureInfo                 _culture;
 
        internal static readonly object DeferredTargetValue = new NamedObject("DeferredTargetValue"); 
        internal static readonly object DeferredSourceValue = new NamedObject("DeferredSourceValue");
 
        // Fields to implement DO's inheritance context
        DependencyObject    _inheritanceContext;
        bool                _hasMultipleInheritanceContexts;
 
        #endregion Private data
 
        #region Private types 

        //------------------------------------------------------ 
        //
        //  Private types
        //
        //----------------------------------------------------- 

        // to support GetValue, we maintain an associative array of all the bindings, 
        // items, and property names that affect a binding group. 
        private class GetValueTable
        { 
            // lookup by item and propertyName
            public GetValueTableEntry this[object item, string propertyName]
            {
                get 
                {
                    for (int i=_table.Count-1; i >= 0; --i) 
                    { 
                        GetValueTableEntry entry = _table[i];
                        if (propertyName == entry.PropertyName && 
                            Object.Equals(item, entry.Item))
                        {
                            return entry;
                        } 
                    }
 
                    return null; 
                }
            } 

            // lookup by binding
            public GetValueTableEntry this[BindingExpressionBase bindingExpressionBase]
            { 
                get
                { 
                    for (int i=_table.Count-1; i >= 0; --i) 
                    {
                        GetValueTableEntry entry = _table[i]; 
                        if (bindingExpressionBase == entry.BindingExpressionBase)
                        {
                            return entry;
                        } 
                    }
 
                    return null; 
                }
            } 

            // ensure an entry for the given binding
            public void EnsureEntry(BindingExpressionBase bindingExpressionBase)
            { 
                GetValueTableEntry entry = this[bindingExpressionBase];
                if (entry == null) 
                { 
                    _table.Add(new GetValueTableEntry(bindingExpressionBase));
                } 
            }

            // update (or add) the entry for the given leaf binding
            public void Update(BindingExpression bindingExpression) 
            {
                GetValueTableEntry entry = this[bindingExpression]; 
                if (entry == null) 
                {
                    _table.Add(new GetValueTableEntry(bindingExpression)); 
                }
                else
                {
                    entry.Update(bindingExpression); 
                }
            } 
 
            // remove all the entries for the given root binding.  Return the list of expressions.
            public List RemoveRootBinding(BindingExpressionBase rootBindingExpression) 
            {
                List result = new List();

                for (int i=_table.Count-1; i >= 0; --i) 
                {
                    BindingExpressionBase expr = _table[i].BindingExpressionBase; 
                    if (expr.RootBindingExpression == rootBindingExpression) 
                    {
                        result.Add(expr); 
                        _table.RemoveAt(i);
                    }
                }
 
                return result;
            } 
 
            // return a list of the unique items (wrapped in WeakReferences)
            public IList UniqueItems() 
            {
                List list = new List();

                for (int i=_table.Count-1; i >= 0; --i) 
                {
                    WeakReference itemWR = _table[i].ItemReference; 
                    if (itemWR != null && BindingGroup.FindIndexOf(itemWR, list) < 0) 
                    {
                        list.Add(itemWR); 
                    }
                }

                return list; 
            }
 
            // get the value for a binding expression 
            public object GetValue(BindingExpressionBase bindingExpressionBase)
            { 
                GetValueTableEntry entry = this[bindingExpressionBase];
                return (entry != null) ? entry.Value : DependencyProperty.UnsetValue;
            }
 
            // set the value for a binding expression
            public void SetValue(BindingExpressionBase bindingExpressionBase, object value) 
            { 
                GetValueTableEntry entry = this[bindingExpressionBase];
                if (entry != null) 
                {
                    entry.Value = value;
                }
            } 

            // reset values to "raw" 
            public void ResetValues() 
            {
                for (int i=_table.Count-1; i>=0; --i) 
                {
                    _table[i].Value = BindingGroup.DeferredTargetValue;
                }
            } 

            // set values to "source" for all bindings under the given root 
            public void UseSourceValue(BindingExpressionBase rootBindingExpression) 
            {
                for (int i=_table.Count-1; i>=0; --i) 
                {
                    if (_table[i].BindingExpressionBase.RootBindingExpression == rootBindingExpression)
                    {
                        _table[i].Value = BindingGroup.DeferredSourceValue; 
                    }
                } 
            } 

            // return the first entry in the table (or null) 
            public GetValueTableEntry GetFirstEntry()
            {
                return (_table.Count > 0) ? _table[0] : null;
            } 

            Collection _table = new Collection(); 
        } 

        // a single entry in the GetValueTable 
        private class GetValueTableEntry
        {
            public GetValueTableEntry(BindingExpressionBase bindingExpressionBase)
            { 
                _bindingExpressionBase = bindingExpressionBase;
            } 
 
            public void Update(BindingExpression bindingExpression)
            { 
                if (_itemWR == null)
                {
                    _itemWR = new WeakReference(bindingExpression.SourceItem);  // WR to avoid leaks
                } 
                else
                { 
                    _itemWR.Target = bindingExpression.SourceItem; 
                }
 
                _propertyName = bindingExpression.SourcePropertyName;
            }

            public object Item 
            {
                get { return _itemWR.Target; } 
            } 

            public WeakReference ItemReference 
            {
                get { return _itemWR; }
            }
 
            public string PropertyName
            { 
                get { return _propertyName; } 
            }
 
            public BindingExpressionBase BindingExpressionBase
            {
                get { return _bindingExpressionBase; }
            } 

            public object Value 
            { 
                get
                { 
                    if (_value == BindingGroup.DeferredTargetValue)
                    {
                        _value = _bindingExpressionBase.RootBindingExpression.GetRawProposedValue();
                    } 
                    else if (_value == BindingGroup.DeferredSourceValue)
                    { 
                        BindingExpression bindingExpression = _bindingExpressionBase as BindingExpression; 
                        Debug.Assert(bindingExpression != null, "do not ask for source value from a [Multi,Priority]Binding");
                        _value = (bindingExpression != null) ? bindingExpression.SourceValue : DependencyProperty.UnsetValue; 
                    }

                    return _value;
                } 
                set { _value = value; }
            } 
 
            BindingExpressionBase   _bindingExpressionBase;
            WeakReference   _itemWR; 
            string          _propertyName;
            object          _value = BindingGroup.DeferredTargetValue;
        }
 
        // add some error-checking to ObservableCollection
        class BindingExpressionCollection : ObservableCollection 
        { 
            /// 
            /// Called by base class Collection<T> when an item is added to list; 
            /// raises a CollectionChanged event to any listeners.
            /// 
            protected override void InsertItem(int index, BindingExpressionBase item)
            { 
                if (item == null)
                { 
                    throw new ArgumentNullException("item"); 
                }
 
                base.InsertItem(index, item);
            }

            ///  
            /// Called by base class Collection<T> when an item is set in list;
            /// raises a CollectionChanged event to any listeners. 
            ///  
            protected override void SetItem(int index, BindingExpressionBase item)
            { 
                if (item == null)
                {
                    throw new ArgumentNullException("item");
                } 

                base.SetItem(index, item); 
            } 
        }
 
        #endregion Private types
    }
}

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