BindingGroup.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Data / BindingGroup.cs / 1471291 / 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;
            _sharesProposedValues = master._sharesProposedValues;
            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; } 
        }
 
        /// 
        /// Enables (or disables) the sharing of proposed values.
        /// 
        ///  
        /// Some UI designs edit multiple properties of a given data item using two
        /// templates for each property - a "display template" that shows the current 
        /// proposed value in non-editable controls, and an "editing template" that 
        /// holds the proposed value in editable controls and allows the user to edit
        /// the value.  As the user moves from one property to another, the templates 
        /// are swapped so that the first property uses its display template while the
        /// second uses its editing template.  The proposed value in the first property's
        /// departing editing template should be preserved ("shared") so that (a) it
        /// can be displayed in the arriving display template, and (b) it can be 
        /// eventually written out to the data item at CommitEdit time.  The BindingGroup
        /// will implement this when SharesProposedValues is true. 
        ///  
        public bool SharesProposedValues
        { 
            get { return _sharesProposedValues; }
            set
            {
                if (_sharesProposedValues != value) 
                {
                    _proposedValueTable.Clear(); 
                    _sharesProposedValues = 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
            { 
                EnsureItems(); 
                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() 
        {
            if (!IsEditing)
            {
                IList items = Items; 
                for (int i=items.Count-1; i>=0; --i)
                { 
                    IEditableObject ieo = items[i] as IEditableObject; 
                    if (ieo != null)
                    { 
                        ieo.BeginEdit();
                    }
                }
 
                IsEditing = true;
            } 
        } 

        ///  
        /// 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() 
        {
            bool result = UpdateAndValidate(ValidationStep.CommittedValue);
            IsEditing = IsEditing && !result;
            return result; 
        }
 
        ///  
        /// 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()
        { 
            // remove validation errors affiliated with the group (errors for 
            // individual bindings will be removed during UpdateTarget)
            ClearValidationErrors(); 

            // 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(); 
            }
 
            // also update dependent targets.  These are one-way bindings that
            // were initialized with a proposed value, and now need to re-fetch
            // the data from their sources
            _proposedValueTable.UpdateDependents(); 

            // remove proposed values 
            _proposedValueTable.Clear(); 

            IsEditing = false; 
        }

        /// 
        /// 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) 
            {
                ProposedValueEntry proposedValueEntry = _proposedValueTable[item, propertyName]; 
                if (proposedValueEntry != null) 
                {
                    // return the proposed value (raw or converted, depending on step) 
                    switch (_validationStep)
                    {
                        case ValidationStep.RawProposedValue:
                            value = proposedValueEntry.RawValue; 
                            return true;
                        case ValidationStep.ConvertedProposedValue: 
                        case ValidationStep.UpdatedValue: 
                        case ValidationStep.CommittedValue:
                            value = proposedValueEntry.ConvertedValue; 
                            return (value != DependencyProperty.UnsetValue);
                    }
                }
 
                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 );

            // if there's a validation rule that should run on data transfer, schedule it to run 
            if (property == FrameworkElement.BindingGroupProperty &&
                !_hasMultipleInheritanceContexts && 
                ValidatesOnDataTransfer) 
            {
                UIElement layoutElement = Helper.FindMentor(this) as UIElement; 
                if (layoutElement != null)
                {
                    // do the validation at the end of the current layout pass, to allow
                    // bindings to join the group 
                    layoutElement.LayoutUpdated += new EventHandler(OnLayoutUpdated);
                } 
            } 

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

        bool IsEditing { get; set; } 

        bool IsItemsValid
        {
            get { return _isItemsValid; } 
            set
            { 
                _isItemsValid = value; 
                if (!value && IsEditing)
                { 
                    // re-evaluate items, in case new items need BeginEdit
                    EnsureItems();
                }
            } 
        }
 
        #endregion Internal properties 

        #region Internal methods 

        //-----------------------------------------------------
        //
        //  Internal methods 
        //
        //----------------------------------------------------- 
 
        // called when a leaf binding changes its source item
        internal void UpdateTable(BindingExpression bindingExpression) 
        {
            bool newEntry = _getValueTable.Update(bindingExpression);
            IsItemsValid = false;
 
            if (newEntry)
            { 
                // once we get an active binding, we no longer need a proposed 
                // value for its source property.
                _proposedValueTable.Remove(bindingExpression); 
            }
        }

        // 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); 
        }
 
        // get the proposed value for the given 
        internal ProposedValueEntry GetProposedValueEntry(object item, string propertyName)
        {
            return _proposedValueTable[item, propertyName]; 
        }
 
        // remove a proposed value entry 
        internal void RemoveProposedValueEntry(ProposedValueEntry entry)
        { 
            _proposedValueTable.Remove(entry);
        }

        // add a dependent on a proposed value 
        internal void AddBindingForProposedValue(BindingExpressionBase dependent, object item, string propertyName)
        { 
            ProposedValueEntry entry = _proposedValueTable[item, propertyName]; 
            if (entry != null)
            { 
                entry.AddDependent(dependent);
            }
        }
 
        // 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) 
        {
            ClearValidationErrorsImpl(validationStep, false);
        }
 
        // remove all errors affiliated with the BindingGroup
        void ClearValidationErrors() 
        { 
            ClearValidationErrorsImpl(ValidationStep.RawProposedValue, true);
        } 

        // remove validation errors - the real work
        void ClearValidationErrorsImpl(ValidationStep validationStep, bool allSteps)
        { 
            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 (allSteps || validationError.RuleInError.ValidationStep == validationStep) 
                {
                    if (validationError.BindingInError == this || 
                        _proposedValueTable.HasValidationError(validationError))
                    {
                        RemoveValidationError(validationError);
                    } 
                }
            } 
        } 

        #endregion Internal methods 

        #region Private methods

        //----------------------------------------------------- 
        //
        //  Private methods 
        // 
        //------------------------------------------------------
 
        // rebuild the Items collection, if necessary
        void EnsureItems()
        {
            if (IsItemsValid) 
                return;
 
            // find the new set of items 
            IList newItems = new Collection();
 
            // always include the DataContext item.  This is necessary for
            // scenarios when there is an item-level validation rule (e.g. DataError)
            // but no edits pending on the item and no two-way bindings.  This
            // arises in DataGrid. 
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor != null) 
            { 
                object dataContextItem = mentor.GetValue(FrameworkElement.DataContextProperty);
                if (dataContextItem != null && 
                    dataContextItem != CollectionView.NewItemPlaceholder &&
                    dataContextItem != BindingExpressionBase.DisconnectedItem)
                {
                    WeakReference itemReference = _itemsRW.Count > 0 ? _itemsRW[0] : null; 
                    // 90% case:  the first entry in _itemsRW already points to the item,
                    // so just re-use it.  Otherwise create a new reference. 
                    if (itemReference == null || 
                        !Object.Equals(dataContextItem, itemReference.Target))
                    { 
                        itemReference = new WeakReference(dataContextItem);
                    }

                    newItems.Add(itemReference); 
                }
            } 
 
            // include items from active two-way bindings and from proposed values
            _getValueTable.AddUniqueItems(newItems); 
            _proposedValueTable.AddUniqueItems(newItems);

            // 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]);
 
                // the new item may need BeginEdit
                if (IsEditing) 
                { 
                    IEditableObject ieo = newItems[i].Target as IEditableObject;
                    if (ieo != null) 
                    {
                        ieo.BeginEdit();
                    }
                } 
            }
 
            IsItemsValid = true; 
        }
 
        // true if there is a validation rule that runs on data transfer
        bool ValidatesOnDataTransfer
        {
            get 
            {
                if (ValidationRules != null) 
                { 
                    for (int i=ValidationRules.Count-1; i>=0; --i)
                    { 
                        if (ValidationRules[i].ValidatesOnTargetUpdated)
                            return true;
                    }
                } 

                return false; 
            } 
        }
 
        // at the first LayoutUpdated event, set up the data-transfer validation process
        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            DependencyObject mentor = Helper.FindMentor(this); 

            // only do this once 
            UIElement layoutElement = mentor as UIElement; 
            if (layoutElement != null)
            { 
                layoutElement.LayoutUpdated -= new EventHandler(OnLayoutUpdated);
            }

            // do the validation every time the DataContext changes 
            FrameworkElement fe;
            FrameworkContentElement fce; 
            Helper.DowncastToFEorFCE(mentor, out fe, out fce, false); 
            if (fe != null)
            { 
                fe.DataContextChanged += new DependencyPropertyChangedEventHandler(OnDataContextChanged);
            }
            else if (fce != null)
            { 
                fce.DataContextChanged += new DependencyPropertyChangedEventHandler(OnDataContextChanged);
            } 
 
            // do the initial validation
            ValidateOnDataTransfer(); 
        }

        void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        { 
            IsItemsValid = false;
            ValidateOnDataTransfer(); 
        } 

        // run the data-transfer validation rules 
        void ValidateOnDataTransfer()
        {
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor == null || ValidationRules.Count == 0) 
                return;
 
            // get the current validation errors associated with the rules to be run. 
            // Eventually we will remove these.
            Collection oldErrors; 
            if (!Validation.GetHasError(mentor))
            {
                // usually there aren't any errors at all
                oldErrors = null; 
            }
            else 
            { 
                // pick out the errors that come from data-transfer rules associated with this BindingGroup
                oldErrors = new Collection(); 
                ReadOnlyCollection errors = Validation.GetErrors(mentor);
                for (int i=0, n=errors.Count; i= ValidationStep.UpdatedValue)); 

            bool result = true; 
 
            for (_validationStep = ValidationStep.RawProposedValue;
                    _validationStep <= validationStep && result; 
                    ++ _validationStep)
            {
                switch (_validationStep)
                { 
                    case ValidationStep.RawProposedValue:
                        _getValueTable.ResetValues(); 
                        break; 
                    case ValidationStep.ConvertedProposedValue:
                        result = ObtainConvertedProposedValues(); 
                        break;
                    case ValidationStep.UpdatedValue:
                        result = UpdateValues();
                        break; 
                    case ValidationStep.CommittedValue:
                        result = CommitValues(); 
                        break; 
                }
 
                if (!CheckValidationRules())
                {
                    result = false;
                } 
            }
 
            ResetProposedValuesAfterUpdate(mentor, result && validationStep == ValidationStep.CommittedValue); 

            _validationStep = (ValidationStep)(-1); 
            _getValueTable.ResetValues();

            return result;
        } 

        // apply conversions to each binding in the group 
        bool ObtainConvertedProposedValues() 
        {
            bool result = true; 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            {
                result = _bindingExpressions[i].ObtainConvertedProposedValue(this) && result;
            } 

            return result; 
        } 

        // update the source value of each binding in the group 
        bool UpdateValues()
        {
            bool result = true;
 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            { 
                result = _bindingExpressions[i].UpdateSource(this) && result; 
            }
 
            if (_proposedValueBindingExpressions != null)
            {
                for (int i=_proposedValueBindingExpressions.Length-1; i>=0; --i)
                { 
                    BindingExpression bindExpr = _proposedValueBindingExpressions[i];
                    ProposedValueEntry proposedValueEntry = _proposedValueTable[bindExpr]; 
                    result = (bindExpr.UpdateSource(proposedValueEntry.ConvertedValue) != DependencyProperty.UnsetValue) 
                                && result;
                } 
            }

            return result;
        } 

        // 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;
                }
            }
 
            // include the bindings for proposed values, for the last two steps
            if (_validationStep >= ValidationStep.UpdatedValue && 
                _proposedValueBindingExpressions != null) 
            {
                for (int i=_proposedValueBindingExpressions.Length-1; i>=0; --i) 
                {
                    if (!_proposedValueBindingExpressions[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)
                { 
                    // 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 IsCritical[Application]Exception() handles these correctly. 
                    #pragma warning disable 56500 

                    try 
                    {
                        ieo.EndEdit();
                    }
                    catch (Exception ex) 
                    {
                        if (CriticalExceptions.IsCriticalApplicationException(ex)) 
                            throw; 

                        ValidationError error = new ValidationError(ExceptionValidationRule.Instance, this, ex.Message, ex); 
                        AddValidationError(error);
                        result = false;
                    }
 
                    #pragma warning restore 56500
                    #pragma warning restore 1634, 1691 
                } 
            }
            return result; 
        }

        // 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 proposedValues;
                root.ValidateAndConvertProposedValue(out proposedValues);
                PreserveProposedValues(proposedValues);
            } 

            // remove the binding expressions from the value table 
            List 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);
            } 
        }

        // preserve proposed values
        void PreserveProposedValues(Collection proposedValues) 
        {
            if (proposedValues == null) 
                return; 

            for (int i=0, n=proposedValues.Count; i rules = originalBinding.ValidationRulesInternal; 
                    if (rules != null)
                    { 
                        for (int j=0, n=rules.Count; j   _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 bool Update(BindingExpression bindingExpression) 
            { 
                GetValueTableEntry entry = this[bindingExpression];
                bool newEntry = (entry == null); 

                if (newEntry)
                {
                    _table.Add(new GetValueTableEntry(bindingExpression)); 
                }
                else 
                { 
                    entry.Update(bindingExpression);
                } 

                return newEntry;
            }
 
            // 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;
            }
 
            // append to a list of the unique items (wrapped in WeakReferences)
            public void AddUniqueItems(IList list) 
            { 
                for (int i=_table.Count-1; i >= 0; --i)
                { 
                    // don't include bindings that couldn't resolve
                    if (_table[i].BindingExpressionBase.Status == BindingStatus.PathError)
                        continue;
 
                    WeakReference itemWR = _table[i].ItemReference;
                    if (itemWR != null && BindingGroup.FindIndexOf(itemWR, list) < 0) 
                    { 
                        list.Add(itemWR);
                    } 
                }
            }

            // 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)
            { 
                object item = bindingExpression.SourceItem; 
                if (item == null)
                { 
                    _itemWR = null;
                }
                else if (_itemWR == null)
                { 
                    _itemWR = new WeakReference(item);  // 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; 
        }
 
 
        // to support sharing of proposed values, we maintain an associative array
        // of  
        private class ProposedValueTable
        {
            // add an entry, based on the ProposedValue structure returned by validation
            public void Add(BindingExpressionBase.ProposedValue proposedValue) 
            {
                BindingExpression bindExpr = proposedValue.BindingExpression; 
                object item = bindExpr.SourceItem; 
                string propertyName = bindExpr.SourcePropertyName;
                object rawValue = proposedValue.RawValue; 
                object convertedValue = proposedValue.ConvertedValue;

                // at most one proposed value per 
                Remove(item, propertyName); 

                // add the new entry 
                _table.Add(new ProposedValueEntry(item, propertyName, rawValue, convertedValue, bindExpr)); 
            }
 
            // remove an entry
            public void Remove(object item, string propertyName)
            {
                int index = IndexOf(item, propertyName); 
                if (index >= 0)
                { 
                    _table.RemoveAt(index); 
                }
            } 

            // remove an entry corresponding to a binding
            public void Remove(BindingExpression bindExpr)
            { 
                if (_table.Count > 0)
                { 
                    Remove(bindExpr.SourceItem, bindExpr.SourcePropertyName); 
                }
            } 

            // remove an entry
            public void Remove(ProposedValueEntry entry)
            { 
                _table.Remove(entry);
            } 
 
            // remove all entries
            public void Clear() 
            {
                _table.Clear();
            }
 
            public int Count { get { return _table.Count; } }
 
            // lookup by item and propertyName 
            public ProposedValueEntry this[object item, string propertyName]
            { 
                get
                {
                    int index = IndexOf(item, propertyName);
                    return (index < 0) ? null : _table[index]; 
                }
            } 
 
            // lookup by index
            public ProposedValueEntry this[int index] 
            {
                get { return _table[index]; }
            }
 
            // lookup by BindingExpression
            public ProposedValueEntry this[BindingExpression bindExpr] 
            { 
                get { return this[bindExpr.SourceItem, bindExpr.SourcePropertyName]; }
            } 

            // append to a list of unique items
            public void AddUniqueItems(IList 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);
                    }
                }
            } 

            // call UpdateTarget on all dependents 
            public void UpdateDependents() 
            {
                for (int i=_table.Count-1; i>=0; --i) 
                {
                    Collection dependents = _table[i].Dependents;
                    if (dependents != null)
                    { 
                        for (int j=dependents.Count-1; j>=0; --j)
                        { 
                            BindingExpressionBase beb = dependents[j]; 
                            if (beb.Status != BindingStatus.Detached)
                            { 
                                dependents[j].UpdateTarget();
                            }
                        }
                    } 
                }
            } 
 
            public bool HasValidationError(ValidationError validationError)
            { 
                for (int i=_table.Count-1; i>=0; --i)
                {
                    if (validationError == _table[i].ValidationError)
                        return true; 
                }
                return false; 
            } 

            // return the index of the entry with given key (or -1) 
            private int IndexOf(object item, string propertyName)
            {
                for (int i=_table.Count-1; i >= 0; --i)
                { 
                    ProposedValueEntry entry = _table[i];
                    if (propertyName == entry.PropertyName && 
                        Object.Equals(item, entry.Item)) 
                    {
                        return i; 
                    }
                }

                return -1; 
            }
 
            Collection _table = new Collection(); 
        }
 
        // a single entry in the ProposedValueTable
        internal class ProposedValueEntry
        {
            public ProposedValueEntry(object item, 
                                    string propertyName,
                                    object rawValue, 
                                    object convertedValue, 
                                    BindingExpression bindExpr)
            { 
                _itemReference = new WeakReference(item);
                _propertyName = propertyName;
                _rawValue = rawValue;
                _convertedValue = convertedValue; 
                _error = bindExpr.ValidationError;
                _binding = bindExpr.ParentBinding; 
            } 

            public object Item                      { get { return _itemReference.Target; } } 
            public string PropertyName              { get { return _propertyName; } }
            public object RawValue                  { get { return _rawValue; } }
            public object ConvertedValue            { get { return _convertedValue; } }
            public ValidationError ValidationError  { get { return _error; } } 
            public Binding Binding                  { get { return _binding; } }
            public WeakReference ItemReference      { get { return _itemReference; } } 
            public Collection Dependents { get { return _dependents; } } 

            public void AddDependent(BindingExpressionBase dependent) 
            {
                if (_dependents == null)
                {
                    _dependents = new Collection(); 
                }
                _dependents.Add(dependent); 
            } 

            WeakReference _itemReference; 
            string _propertyName;
            object _rawValue;
            object _convertedValue;
            ValidationError _error; 
            Binding _binding;
            Collection _dependents; 
        } 

        // 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;
            _sharesProposedValues = master._sharesProposedValues;
            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; } 
        }
 
        /// 
        /// Enables (or disables) the sharing of proposed values.
        /// 
        ///  
        /// Some UI designs edit multiple properties of a given data item using two
        /// templates for each property - a "display template" that shows the current 
        /// proposed value in non-editable controls, and an "editing template" that 
        /// holds the proposed value in editable controls and allows the user to edit
        /// the value.  As the user moves from one property to another, the templates 
        /// are swapped so that the first property uses its display template while the
        /// second uses its editing template.  The proposed value in the first property's
        /// departing editing template should be preserved ("shared") so that (a) it
        /// can be displayed in the arriving display template, and (b) it can be 
        /// eventually written out to the data item at CommitEdit time.  The BindingGroup
        /// will implement this when SharesProposedValues is true. 
        ///  
        public bool SharesProposedValues
        { 
            get { return _sharesProposedValues; }
            set
            {
                if (_sharesProposedValues != value) 
                {
                    _proposedValueTable.Clear(); 
                    _sharesProposedValues = 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
            { 
                EnsureItems(); 
                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() 
        {
            if (!IsEditing)
            {
                IList items = Items; 
                for (int i=items.Count-1; i>=0; --i)
                { 
                    IEditableObject ieo = items[i] as IEditableObject; 
                    if (ieo != null)
                    { 
                        ieo.BeginEdit();
                    }
                }
 
                IsEditing = true;
            } 
        } 

        ///  
        /// 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() 
        {
            bool result = UpdateAndValidate(ValidationStep.CommittedValue);
            IsEditing = IsEditing && !result;
            return result; 
        }
 
        ///  
        /// 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()
        { 
            // remove validation errors affiliated with the group (errors for 
            // individual bindings will be removed during UpdateTarget)
            ClearValidationErrors(); 

            // 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(); 
            }
 
            // also update dependent targets.  These are one-way bindings that
            // were initialized with a proposed value, and now need to re-fetch
            // the data from their sources
            _proposedValueTable.UpdateDependents(); 

            // remove proposed values 
            _proposedValueTable.Clear(); 

            IsEditing = false; 
        }

        /// 
        /// 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) 
            {
                ProposedValueEntry proposedValueEntry = _proposedValueTable[item, propertyName]; 
                if (proposedValueEntry != null) 
                {
                    // return the proposed value (raw or converted, depending on step) 
                    switch (_validationStep)
                    {
                        case ValidationStep.RawProposedValue:
                            value = proposedValueEntry.RawValue; 
                            return true;
                        case ValidationStep.ConvertedProposedValue: 
                        case ValidationStep.UpdatedValue: 
                        case ValidationStep.CommittedValue:
                            value = proposedValueEntry.ConvertedValue; 
                            return (value != DependencyProperty.UnsetValue);
                    }
                }
 
                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 );

            // if there's a validation rule that should run on data transfer, schedule it to run 
            if (property == FrameworkElement.BindingGroupProperty &&
                !_hasMultipleInheritanceContexts && 
                ValidatesOnDataTransfer) 
            {
                UIElement layoutElement = Helper.FindMentor(this) as UIElement; 
                if (layoutElement != null)
                {
                    // do the validation at the end of the current layout pass, to allow
                    // bindings to join the group 
                    layoutElement.LayoutUpdated += new EventHandler(OnLayoutUpdated);
                } 
            } 

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

        bool IsEditing { get; set; } 

        bool IsItemsValid
        {
            get { return _isItemsValid; } 
            set
            { 
                _isItemsValid = value; 
                if (!value && IsEditing)
                { 
                    // re-evaluate items, in case new items need BeginEdit
                    EnsureItems();
                }
            } 
        }
 
        #endregion Internal properties 

        #region Internal methods 

        //-----------------------------------------------------
        //
        //  Internal methods 
        //
        //----------------------------------------------------- 
 
        // called when a leaf binding changes its source item
        internal void UpdateTable(BindingExpression bindingExpression) 
        {
            bool newEntry = _getValueTable.Update(bindingExpression);
            IsItemsValid = false;
 
            if (newEntry)
            { 
                // once we get an active binding, we no longer need a proposed 
                // value for its source property.
                _proposedValueTable.Remove(bindingExpression); 
            }
        }

        // 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); 
        }
 
        // get the proposed value for the given 
        internal ProposedValueEntry GetProposedValueEntry(object item, string propertyName)
        {
            return _proposedValueTable[item, propertyName]; 
        }
 
        // remove a proposed value entry 
        internal void RemoveProposedValueEntry(ProposedValueEntry entry)
        { 
            _proposedValueTable.Remove(entry);
        }

        // add a dependent on a proposed value 
        internal void AddBindingForProposedValue(BindingExpressionBase dependent, object item, string propertyName)
        { 
            ProposedValueEntry entry = _proposedValueTable[item, propertyName]; 
            if (entry != null)
            { 
                entry.AddDependent(dependent);
            }
        }
 
        // 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) 
        {
            ClearValidationErrorsImpl(validationStep, false);
        }
 
        // remove all errors affiliated with the BindingGroup
        void ClearValidationErrors() 
        { 
            ClearValidationErrorsImpl(ValidationStep.RawProposedValue, true);
        } 

        // remove validation errors - the real work
        void ClearValidationErrorsImpl(ValidationStep validationStep, bool allSteps)
        { 
            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 (allSteps || validationError.RuleInError.ValidationStep == validationStep) 
                {
                    if (validationError.BindingInError == this || 
                        _proposedValueTable.HasValidationError(validationError))
                    {
                        RemoveValidationError(validationError);
                    } 
                }
            } 
        } 

        #endregion Internal methods 

        #region Private methods

        //----------------------------------------------------- 
        //
        //  Private methods 
        // 
        //------------------------------------------------------
 
        // rebuild the Items collection, if necessary
        void EnsureItems()
        {
            if (IsItemsValid) 
                return;
 
            // find the new set of items 
            IList newItems = new Collection();
 
            // always include the DataContext item.  This is necessary for
            // scenarios when there is an item-level validation rule (e.g. DataError)
            // but no edits pending on the item and no two-way bindings.  This
            // arises in DataGrid. 
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor != null) 
            { 
                object dataContextItem = mentor.GetValue(FrameworkElement.DataContextProperty);
                if (dataContextItem != null && 
                    dataContextItem != CollectionView.NewItemPlaceholder &&
                    dataContextItem != BindingExpressionBase.DisconnectedItem)
                {
                    WeakReference itemReference = _itemsRW.Count > 0 ? _itemsRW[0] : null; 
                    // 90% case:  the first entry in _itemsRW already points to the item,
                    // so just re-use it.  Otherwise create a new reference. 
                    if (itemReference == null || 
                        !Object.Equals(dataContextItem, itemReference.Target))
                    { 
                        itemReference = new WeakReference(dataContextItem);
                    }

                    newItems.Add(itemReference); 
                }
            } 
 
            // include items from active two-way bindings and from proposed values
            _getValueTable.AddUniqueItems(newItems); 
            _proposedValueTable.AddUniqueItems(newItems);

            // 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]);
 
                // the new item may need BeginEdit
                if (IsEditing) 
                { 
                    IEditableObject ieo = newItems[i].Target as IEditableObject;
                    if (ieo != null) 
                    {
                        ieo.BeginEdit();
                    }
                } 
            }
 
            IsItemsValid = true; 
        }
 
        // true if there is a validation rule that runs on data transfer
        bool ValidatesOnDataTransfer
        {
            get 
            {
                if (ValidationRules != null) 
                { 
                    for (int i=ValidationRules.Count-1; i>=0; --i)
                    { 
                        if (ValidationRules[i].ValidatesOnTargetUpdated)
                            return true;
                    }
                } 

                return false; 
            } 
        }
 
        // at the first LayoutUpdated event, set up the data-transfer validation process
        private void OnLayoutUpdated(object sender, EventArgs e)
        {
            DependencyObject mentor = Helper.FindMentor(this); 

            // only do this once 
            UIElement layoutElement = mentor as UIElement; 
            if (layoutElement != null)
            { 
                layoutElement.LayoutUpdated -= new EventHandler(OnLayoutUpdated);
            }

            // do the validation every time the DataContext changes 
            FrameworkElement fe;
            FrameworkContentElement fce; 
            Helper.DowncastToFEorFCE(mentor, out fe, out fce, false); 
            if (fe != null)
            { 
                fe.DataContextChanged += new DependencyPropertyChangedEventHandler(OnDataContextChanged);
            }
            else if (fce != null)
            { 
                fce.DataContextChanged += new DependencyPropertyChangedEventHandler(OnDataContextChanged);
            } 
 
            // do the initial validation
            ValidateOnDataTransfer(); 
        }

        void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
        { 
            IsItemsValid = false;
            ValidateOnDataTransfer(); 
        } 

        // run the data-transfer validation rules 
        void ValidateOnDataTransfer()
        {
            DependencyObject mentor = Helper.FindMentor(this);
            if (mentor == null || ValidationRules.Count == 0) 
                return;
 
            // get the current validation errors associated with the rules to be run. 
            // Eventually we will remove these.
            Collection oldErrors; 
            if (!Validation.GetHasError(mentor))
            {
                // usually there aren't any errors at all
                oldErrors = null; 
            }
            else 
            { 
                // pick out the errors that come from data-transfer rules associated with this BindingGroup
                oldErrors = new Collection(); 
                ReadOnlyCollection errors = Validation.GetErrors(mentor);
                for (int i=0, n=errors.Count; i= ValidationStep.UpdatedValue)); 

            bool result = true; 
 
            for (_validationStep = ValidationStep.RawProposedValue;
                    _validationStep <= validationStep && result; 
                    ++ _validationStep)
            {
                switch (_validationStep)
                { 
                    case ValidationStep.RawProposedValue:
                        _getValueTable.ResetValues(); 
                        break; 
                    case ValidationStep.ConvertedProposedValue:
                        result = ObtainConvertedProposedValues(); 
                        break;
                    case ValidationStep.UpdatedValue:
                        result = UpdateValues();
                        break; 
                    case ValidationStep.CommittedValue:
                        result = CommitValues(); 
                        break; 
                }
 
                if (!CheckValidationRules())
                {
                    result = false;
                } 
            }
 
            ResetProposedValuesAfterUpdate(mentor, result && validationStep == ValidationStep.CommittedValue); 

            _validationStep = (ValidationStep)(-1); 
            _getValueTable.ResetValues();

            return result;
        } 

        // apply conversions to each binding in the group 
        bool ObtainConvertedProposedValues() 
        {
            bool result = true; 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            {
                result = _bindingExpressions[i].ObtainConvertedProposedValue(this) && result;
            } 

            return result; 
        } 

        // update the source value of each binding in the group 
        bool UpdateValues()
        {
            bool result = true;
 
            for (int i=_bindingExpressions.Count-1; i>=0; --i)
            { 
                result = _bindingExpressions[i].UpdateSource(this) && result; 
            }
 
            if (_proposedValueBindingExpressions != null)
            {
                for (int i=_proposedValueBindingExpressions.Length-1; i>=0; --i)
                { 
                    BindingExpression bindExpr = _proposedValueBindingExpressions[i];
                    ProposedValueEntry proposedValueEntry = _proposedValueTable[bindExpr]; 
                    result = (bindExpr.UpdateSource(proposedValueEntry.ConvertedValue) != DependencyProperty.UnsetValue) 
                                && result;
                } 
            }

            return result;
        } 

        // 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;
                }
            }
 
            // include the bindings for proposed values, for the last two steps
            if (_validationStep >= ValidationStep.UpdatedValue && 
                _proposedValueBindingExpressions != null) 
            {
                for (int i=_proposedValueBindingExpressions.Length-1; i>=0; --i) 
                {
                    if (!_proposedValueBindingExpressions[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)
                { 
                    // 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 IsCritical[Application]Exception() handles these correctly. 
                    #pragma warning disable 56500 

                    try 
                    {
                        ieo.EndEdit();
                    }
                    catch (Exception ex) 
                    {
                        if (CriticalExceptions.IsCriticalApplicationException(ex)) 
                            throw; 

                        ValidationError error = new ValidationError(ExceptionValidationRule.Instance, this, ex.Message, ex); 
                        AddValidationError(error);
                        result = false;
                    }
 
                    #pragma warning restore 56500
                    #pragma warning restore 1634, 1691 
                } 
            }
            return result; 
        }

        // 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 proposedValues;
                root.ValidateAndConvertProposedValue(out proposedValues);
                PreserveProposedValues(proposedValues);
            } 

            // remove the binding expressions from the value table 
            List 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);
            } 
        }

        // preserve proposed values
        void PreserveProposedValues(Collection proposedValues) 
        {
            if (proposedValues == null) 
                return; 

            for (int i=0, n=proposedValues.Count; i rules = originalBinding.ValidationRulesInternal; 
                    if (rules != null)
                    { 
                        for (int j=0, n=rules.Count; j   _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 bool Update(BindingExpression bindingExpression) 
            { 
                GetValueTableEntry entry = this[bindingExpression];
                bool newEntry = (entry == null); 

                if (newEntry)
                {
                    _table.Add(new GetValueTableEntry(bindingExpression)); 
                }
                else 
                { 
                    entry.Update(bindingExpression);
                } 

                return newEntry;
            }
 
            // 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;
            }
 
            // append to a list of the unique items (wrapped in WeakReferences)
            public void AddUniqueItems(IList list) 
            { 
                for (int i=_table.Count-1; i >= 0; --i)
                { 
                    // don't include bindings that couldn't resolve
                    if (_table[i].BindingExpressionBase.Status == BindingStatus.PathError)
                        continue;
 
                    WeakReference itemWR = _table[i].ItemReference;
                    if (itemWR != null && BindingGroup.FindIndexOf(itemWR, list) < 0) 
                    { 
                        list.Add(itemWR);
                    } 
                }
            }

            // 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)
            { 
                object item = bindingExpression.SourceItem; 
                if (item == null)
                { 
                    _itemWR = null;
                }
                else if (_itemWR == null)
                { 
                    _itemWR = new WeakReference(item);  // 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; 
        }
 
 
        // to support sharing of proposed values, we maintain an associative array
        // of  
        private class ProposedValueTable
        {
            // add an entry, based on the ProposedValue structure returned by validation
            public void Add(BindingExpressionBase.ProposedValue proposedValue) 
            {
                BindingExpression bindExpr = proposedValue.BindingExpression; 
                object item = bindExpr.SourceItem; 
                string propertyName = bindExpr.SourcePropertyName;
                object rawValue = proposedValue.RawValue; 
                object convertedValue = proposedValue.ConvertedValue;

                // at most one proposed value per 
                Remove(item, propertyName); 

                // add the new entry 
                _table.Add(new ProposedValueEntry(item, propertyName, rawValue, convertedValue, bindExpr)); 
            }
 
            // remove an entry
            public void Remove(object item, string propertyName)
            {
                int index = IndexOf(item, propertyName); 
                if (index >= 0)
                { 
                    _table.RemoveAt(index); 
                }
            } 

            // remove an entry corresponding to a binding
            public void Remove(BindingExpression bindExpr)
            { 
                if (_table.Count > 0)
                { 
                    Remove(bindExpr.SourceItem, bindExpr.SourcePropertyName); 
                }
            } 

            // remove an entry
            public void Remove(ProposedValueEntry entry)
            { 
                _table.Remove(entry);
            } 
 
            // remove all entries
            public void Clear() 
            {
                _table.Clear();
            }
 
            public int Count { get { return _table.Count; } }
 
            // lookup by item and propertyName 
            public ProposedValueEntry this[object item, string propertyName]
            { 
                get
                {
                    int index = IndexOf(item, propertyName);
                    return (index < 0) ? null : _table[index]; 
                }
            } 
 
            // lookup by index
            public ProposedValueEntry this[int index] 
            {
                get { return _table[index]; }
            }
 
            // lookup by BindingExpression
            public ProposedValueEntry this[BindingExpression bindExpr] 
            { 
                get { return this[bindExpr.SourceItem, bindExpr.SourcePropertyName]; }
            } 

            // append to a list of unique items
            public void AddUniqueItems(IList 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);
                    }
                }
            } 

            // call UpdateTarget on all dependents 
            public void UpdateDependents() 
            {
                for (int i=_table.Count-1; i>=0; --i) 
                {
                    Collection dependents = _table[i].Dependents;
                    if (dependents != null)
                    { 
                        for (int j=dependents.Count-1; j>=0; --j)
                        { 
                            BindingExpressionBase beb = dependents[j]; 
                            if (beb.Status != BindingStatus.Detached)
                            { 
                                dependents[j].UpdateTarget();
                            }
                        }
                    } 
                }
            } 
 
            public bool HasValidationError(ValidationError validationError)
            { 
                for (int i=_table.Count-1; i>=0; --i)
                {
                    if (validationError == _table[i].ValidationError)
                        return true; 
                }
                return false; 
            } 

            // return the index of the entry with given key (or -1) 
            private int IndexOf(object item, string propertyName)
            {
                for (int i=_table.Count-1; i >= 0; --i)
                { 
                    ProposedValueEntry entry = _table[i];
                    if (propertyName == entry.PropertyName && 
                        Object.Equals(item, entry.Item)) 
                    {
                        return i; 
                    }
                }

                return -1; 
            }
 
            Collection _table = new Collection(); 
        }
 
        // a single entry in the ProposedValueTable
        internal class ProposedValueEntry
        {
            public ProposedValueEntry(object item, 
                                    string propertyName,
                                    object rawValue, 
                                    object convertedValue, 
                                    BindingExpression bindExpr)
            { 
                _itemReference = new WeakReference(item);
                _propertyName = propertyName;
                _rawValue = rawValue;
                _convertedValue = convertedValue; 
                _error = bindExpr.ValidationError;
                _binding = bindExpr.ParentBinding; 
            } 

            public object Item                      { get { return _itemReference.Target; } } 
            public string PropertyName              { get { return _propertyName; } }
            public object RawValue                  { get { return _rawValue; } }
            public object ConvertedValue            { get { return _convertedValue; } }
            public ValidationError ValidationError  { get { return _error; } } 
            public Binding Binding                  { get { return _binding; } }
            public WeakReference ItemReference      { get { return _itemReference; } } 
            public Collection Dependents { get { return _dependents; } } 

            public void AddDependent(BindingExpressionBase dependent) 
            {
                if (_dependents == null)
                {
                    _dependents = new Collection(); 
                }
                _dependents.Add(dependent); 
            } 

            WeakReference _itemReference; 
            string _propertyName;
            object _rawValue;
            object _convertedValue;
            ValidationError _error; 
            Binding _binding;
            Collection _dependents; 
        } 

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