PropertyPathWorker.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / Data / PropertyPathWorker.cs / 1 / PropertyPathWorker.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: Defines PropertyPathWorker object, workhorse for CLR bindings 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics; 
using System.Reflection;
using System.Globalization; 
using System.Text; 
using System.Windows.Threading;
using System.Windows; 
using System.Windows.Controls;                      // Validation
using System.Windows.Data;
using MS.Internal;
using MS.Internal.Hashing.PresentationFramework;   // HashHelper 

namespace MS.Internal.Data 
{ 
    internal sealed class PropertyPathWorker: IWeakEventListener
    { 
        //-----------------------------------------------------
        //
        //  Constructors
        // 
        //-----------------------------------------------------
 
        internal PropertyPathWorker(PropertyPath path) 
            : this(path, DataBindEngine.CurrentDataBindEngine)
        { 
        }

        internal PropertyPathWorker(PropertyPath path, ClrBindingWorker host, bool isDynamic, DataBindEngine engine)
            : this(path, engine) 
        {
            _host = host; 
            _isDynamic = isDynamic; 
        }
 
        private PropertyPathWorker(PropertyPath path, DataBindEngine engine)
        {
            _parent = path;
            _arySVS = new SourceValueState[path.Length]; 
            _engine = engine;
 
            // initialize each level to NullDataItem, so that the first real 
            // item will force a change
            for (int i=_arySVS.Length-1; i>=0; --i) 
            {
                _arySVS[i].item = BindingExpression.CreateReference(BindingExpression.NullDataItem);
            }
        } 

        //------------------------------------------------------ 
        // 
        //  Internal Properties
        // 
        //-----------------------------------------------------

        internal int Length { get { return _parent.Length; } }
        internal PropertyPathStatus Status { get { return _status; } } 

        internal DependencyObject TreeContext 
        { 
            get { return BindingExpression.GetReference(_treeContext) as DependencyObject; }
            set { _treeContext = BindingExpression.CreateReference(value); } 
        }

        internal void SetTreeContext(WeakReference wr)
        { 
            _treeContext = BindingExpression.CreateReference(wr);
        } 
 
        internal bool IsDBNullValidForUpdate
        { 
            get
            {
                if (!_isDBNullValidForUpdate.HasValue)
                { 
                    DetermineWhetherDBNullIsValid();
                } 
 
                return _isDBNullValidForUpdate.Value;
            } 
        }

        internal object SourceItem
        { 
            get
            { 
                int level = Length-1; 
                return (level >= 0) ? GetItem(level) : null;
            } 
        }

        internal string SourcePropertyName
        { 
            get
            { 
                DependencyProperty dp; 
                PropertyInfo pi;
                PropertyDescriptor pd; 
                int level = Length-1;

                if (level < 0)
                    return null; 

                SetPropertyInfo(GetAccessor(level), out pi, out pd, out dp); 
 
                return  (dp != null) ? dp.Name :
                        (pi != null) ? pi.Name : 
                        (pd != null) ? pd.Name : null;
            }
        }
 
        //------------------------------------------------------
        // 
        //  Internal Methods 
        //
        //------------------------------------------------------ 

        //-------  common methods ------

        internal object GetItem(int level) 
        {
            return BindingExpression.GetReference(_arySVS[level].item); 
        } 

        internal object GetAccessor(int level) 
        {
            return _arySVS[level].info;
        }
 
        internal object[] GetIndexerArguments(int level)
        { 
            return _arySVS[level].args; 
        }
 
        internal Type GetType(int level)
        {
            return _arySVS[level].type;
        } 

        //-------  target mode ------ 
 
        // Set the context for the path.  Use this method in "target" mode
        // to connect the path to a rootItem for a short time: 
        //      using (path.SetContext(myItem))
        //      {
        //          ... call target-mode convenience methods ...
        //      } 
        internal IDisposable SetContext(object rootItem)
        { 
            if (_contextHelper == null) 
                _contextHelper = new ContextHelper(this);
 
            _contextHelper.SetContext(rootItem);
            return _contextHelper;
        }
 
        //-------  source mode (should only be called by ClrBindingWorker) ------
 
        internal void AttachToRootItem(object rootItem) 
        {
            _rootItem = BindingExpression.CreateReference(rootItem); 
            UpdateSourceValueState(-1, null);
        }

        internal void DetachFromRootItem() 
        {
            _rootItem = BindingExpression.NullDataItem; 
            UpdateSourceValueState(-1, null); 
            _rootItem = null;
        } 

        internal object GetValue(object item, int level)
        {
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.GetValue); 
            DependencyProperty dp;
            PropertyInfo pi; 
            PropertyDescriptor pd; 
            object value = DependencyProperty.UnsetValue;
            SetPropertyInfo(_arySVS[level].info, out pi, out pd, out dp); 

            switch (SVI[level].type)
            {
            case SourceValueType.Property: 
                if (pi != null)
                { 
                    value = pi.GetValue(item, null); 
                }
                else if (pd != null) 
                {
                    bool indexerIsNext = (level+1 < SVI.Length && SVI[level+1].type == SourceValueType.Indexer);
                    value = Engine.GetValue(item, pd, indexerIsNext);
                } 
                else if (dp != null)
                { 
                    DependencyObject d = (DependencyObject)item; 
                    if (level != Length-1 || _host == null || _host.TransfersDefaultValue)
                        value = d.GetValue(dp); 
                    else if (!Helper.HasDefaultValue(dp, d, d as FrameworkElement, d as FrameworkContentElement))
                        value = d.GetValue(dp);
                    else
                        value = BindingExpression.IgnoreDefaultValue; 
                }
                break; 
 
            case SourceValueType.Indexer:
                // 
                if (pi != null)
                {
                    value = pi.GetValue(item,
                                    BindingFlags.GetProperty, null, 
                                    _arySVS[level].args,
                                    CultureInfo.InvariantCulture); 
                } 
                else
                { 
                    throw new NotSupportedException(SR.Get(SRID.IndexedPropDescNotImplemented));
                }
                break;
 
            case SourceValueType.Direct:
                value = item; 
                break; 
            }
 
            if (isExtendedTraceEnabled)
            {
                object accessor = _arySVS[level].info;
                if (accessor == DependencyProperty.UnsetValue) 
                    accessor = null;
 
                TraceData.Trace(TraceEventType.Warning, 
                                    TraceData.GetValue(
                                        TraceData.Identify(_host.ParentBindingExpression), 
                                        level,
                                        TraceData.Identify(item),
                                        TraceData.IdentifyAccessor(accessor),
                                        TraceData.Identify(value))); 
            }
 
            return value; 
        }
 
        internal void SetValue(object item, object value)
        {
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.GetValue);
            PropertyInfo pi; 
            PropertyDescriptor pd;
            DependencyProperty dp; 
            int level = _arySVS.Length - 1; 
            SetPropertyInfo(_arySVS[level].info, out pi, out pd, out dp);
 
            if (isExtendedTraceEnabled)
            {
                TraceData.Trace(TraceEventType.Warning,
                                    TraceData.SetValue( 
                                        TraceData.Identify(_host.ParentBindingExpression),
                                        level, 
                                        TraceData.Identify(item), 
                                        TraceData.IdentifyAccessor(_arySVS[level].info),
                                        TraceData.Identify(value))); 
            }

            switch (SVI[level].type)
            { 
            case SourceValueType.Property:
                if (pd != null) 
                { 
                    pd.SetValue(item, value);
                } 
                else if (pi != null)
                {
                    pi.SetValue(item, value, null);
                } 
                else if (dp != null)
                { 
                    ((DependencyObject)item).SetValue(dp, value); 
                }
                break; 

            case SourceValueType.Indexer:
                //
                if (pi != null) 
                {
                    pi.SetValue(item, value, 
                                    BindingFlags.SetProperty, null, 
                                    _arySVS[level].args,
                                    CultureInfo.InvariantCulture); 
                }
                else
                {
                    throw new NotSupportedException(SR.Get(SRID.IndexedPropDescNotImplemented)); 
                }
                break; 
            } 
        }
 
        internal object RawValue()
        {
            object rawValue = RawValue(Length-1);
 
            if (rawValue == AsyncRequestPending)
                rawValue = DependencyProperty.UnsetValue;     // the real value will arrive later 
 
            return rawValue;
        } 

        // Called by BE.UpdateTarget().  Re-fetch the value at each level.
        // If there's a difference, simulate a property-change at that level.
        internal void RefreshValue() 
        {
            for (int k=1; k<_arySVS.Length; ++k) 
            { 
                object oldValue = BindingExpression.GetReference(_arySVS[k].item);
                if (!Object.Equals(oldValue, RawValue(k-1))) 
                {
                    UpdateSourceValueState(k-1, null);
                    return;
                } 
            }
 
            UpdateSourceValueState(Length-1, null); 
        }
 
        // return the source level where the change happened, or -1 if the
        // change is irrelevant.
        internal int LevelForPropertyChange(object item, string propertyName)
        { 
            // This test must be thread-safe - it can get called on the "wrong" context.
            // It's read-only (good).  And if another thread changes the values it reads, 
            // the worst that can happen is to schedule a transfer operation needlessly - 
            // the operation itself won't do anything (since the test is repeated on the
            // right thread). 

            bool isIndexer = propertyName == Binding.IndexerName;

            for (int k=0; k<_arySVS.Length; ++k) 
            {
                if (BindingExpression.GetReference(_arySVS[k].item) == item && 
                        (String.IsNullOrEmpty(propertyName) || 
                         (isIndexer && SVI[k].type == MS.Internal.Data.SourceValueType.Indexer) ||
                         String.Equals(SVI[k].propertyName, propertyName, StringComparison.OrdinalIgnoreCase))) 
                {
                    return k;
                }
            } 

            return -1; 
        } 

        internal void OnPropertyChangedAtLevel(int level) 
        {
            UpdateSourceValueState(level, null);
        }
 
        internal void OnCurrentChanged(ICollectionView collectionView)
        { 
            for (int k=0; k= 0 && SVI[level].type == SourceValueType.Property)
            { 
                object item = GetItem(level);
                IDataErrorInfo idei = item as IDataErrorInfo;
                if (idei != null)
                { 
                    DependencyProperty dp;
                    PropertyInfo pi; 
                    PropertyDescriptor pd; 
                    SetPropertyInfo(GetAccessor(level), out pi, out pd, out dp);
 
                    string name =   (dp != null) ? dp.Name :
                                    (pi != null) ? pi.Name :
                                    (pd != null) ? pd.Name : null;
                    string error; 

                    if (name != null) 
                    { 
                        // get the data error information, if any, by calling idie[name].
                        // We do this in a paranoid way, even though indexers with 
                        // string-valued arguments are not supposed to throw exceptions.

                        // PreSharp uses message numbers that the C# compiler doesn't know about.
                        // Disable the C# complaints, per the PreSharp documentation. 
                        #pragma warning disable 1634, 1691
 
                        // PreSharp complains about catching NullReference (and other) exceptions. 
                        // It doesn't recognize that IsCriticalException() handles these correctly.
                        #pragma warning disable 56500 

                        try
                        {
                            error = idei[name]; 
                        }
                        catch (Exception ex) 
                        { 
                            if (CriticalExceptions.IsCriticalException(ex))
                                throw; 

                            error = null;

                            if (TraceData.IsEnabled) 
                            {
                                TraceData.Trace(TraceEventType.Error, 
                                                TraceData.DataErrorInfoFailed( 
                                                    name,
                                                    item.GetType().FullName, 
                                                    ex.GetType().FullName,
                                                    ex.Message),
                                                bindingExpressionBase);
                            } 
                        }
                        #pragma warning restore 56500 
                        #pragma warning restore 1634, 1691 
                    }
                    else 
                    {
                        error = null;
                    }
 
                    if (!String.IsNullOrEmpty(error))
                    { 
                        result = new ValidationError(DataErrorValidationRule.Instance, 
                                                    bindingExpressionBase,
                                                    error, 
                                                    null);
                    }
                }
            } 

            return result; 
        } 

        //----------------------------------------------------- 
        //
        //  Private Properties
        //
        //------------------------------------------------------ 

        bool IsDynamic { get { return _isDynamic; } } 
        SourceValueInfo[] SVI { get { return _parent.SVI; } } 
        DataBindEngine Engine { get { return _engine; } }
 
        //-----------------------------------------------------
        //
        //  Private Methods
        // 
        //-----------------------------------------------------
 
        // fill in the SourceValueState with updated infomation, starting at level k+1. 
        // If view isn't null, also update the current item at level k.
        private void UpdateSourceValueState(int k, ICollectionView collectionView) 
        {
            UpdateSourceValueState(k, collectionView, BindingExpression.NullDataItem, false);
        }
 
        // fill in the SourceValueState with updated infomation, starting at level k+1.
        // If view isn't null, also update the current item at level k. 
        private void UpdateSourceValueState(int k, ICollectionView collectionView, object newValue, bool isASubPropertyChange) 
        {
            // give host a chance to shut down the binding if the target has 
            // gone away
            DependencyObject target = null;
            if (_host != null)
            { 
                target = _host.CheckTarget();
                if (_rootItem != BindingExpression.NullDataItem && target == null) 
                    return; 
            }
 
            int initialLevel = k;
            object rawValue = null;

            // optimistically assume the new value will fix previous path errors 
            _status = PropertyPathStatus.Active;
 
            // prepare to collect changes to dependency sources 
            _dependencySourcesChanged = false;
 
            // Update the current item at level k, if requested
            if (collectionView != null)
            {
                Debug.Assert(0<=k && k<_arySVS.Length && _arySVS[k].collectionView == collectionView, "bad parameters to UpdateSourceValueState"); 
                ReplaceItem(k, collectionView.CurrentItem, NoParent);
            } 
 
            // update the remaining levels
            for (++k; k<_arySVS.Length; ++k) 
            {
                isASubPropertyChange = false;   // sub-property changes only matter at the last level

                ICollectionView oldCollectionView = _arySVS[k].collectionView; 

                // replace the item at level k using parent from level k-1 
                rawValue = (newValue == BindingExpression.NullDataItem) ? RawValue(k-1) : newValue; 
                newValue = BindingExpression.NullDataItem;
                if (rawValue == AsyncRequestPending) 
                {
                    _status = PropertyPathStatus.AsyncRequestPending;
                    break;      // we'll resume the loop after the request completes
                } 

                ReplaceItem(k, BindingExpression.NullDataItem, rawValue); 
 
                // replace view, if necessary
                ICollectionView newCollectionView = _arySVS[k].collectionView; 
                if (oldCollectionView != newCollectionView && _host != null)
                {
                    _host.ReplaceCurrentItem(oldCollectionView, newCollectionView);
                } 
            }
 
            // notify binding about what happened 
            if (_host != null)
            { 
                _host.NewValueAvailable(_dependencySourcesChanged, initialLevel < 0, isASubPropertyChange);
            }

            GC.KeepAlive(target);   // keep target alive during changes (bug 956831) 
        }
 
        // replace the item at level k with the given item, or with an item obtained from the given parent 
        private void ReplaceItem(int k, object newO, object parent)
        { 
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.ReplaceItem);
            SourceValueState svs = new SourceValueState();

            object oldO = BindingExpression.GetReference(_arySVS[k].item); 

            // stop listening to old item 
            if (IsDynamic && SVI[k].type != SourceValueType.Direct) 
            {
                INotifyPropertyChanged oldPC = oldO as INotifyPropertyChanged; 
                if (oldPC != null)
                {
                    PropertyChangedEventManager.RemoveListener(oldPC, this, SVI[k].propertyName);
                } 
                else
                { 
                    PropertyDescriptor oldDesc = _arySVS[k].info as PropertyDescriptor; 
                    if (oldDesc != null && oldO != null && oldO != BindingExpression.NullDataItem)
                    { 
                        ValueChangedEventManager.RemoveListener(oldO, this, oldDesc);
                    }
                }
 
                DependencyProperty dp = _arySVS[k].info as DependencyProperty;
                if (dp != null) 
                { 
                    _dependencySourcesChanged = true;
                } 
            }

            // clear the IsDBNullValid cache
            _isDBNullValidForUpdate = null; 

            if (newO == null || parent == DependencyProperty.UnsetValue || parent == BindingExpression.NullDataItem) 
            { 
                _arySVS[k].item = BindingExpression.ReplaceReference(_arySVS[k].item, newO);
 
                if (parent == DependencyProperty.UnsetValue || parent == BindingExpression.NullDataItem)
                    _arySVS[k].collectionView = null;

                if (isExtendedTraceEnabled) 
                {
                    TraceData.Trace(TraceEventType.Warning, 
                                        TraceData.ReplaceItemShort( 
                                            TraceData.Identify(_host.ParentBindingExpression),
                                            k, 
                                            TraceData.Identify(newO)));
                }

                return; 
            }
 
            // obtain the new item and its access info 
            if (newO != BindingExpression.NullDataItem)
            { 
                GetInfo(k, newO, ref svs);
                svs.collectionView = _arySVS[k].collectionView;
            }
            else 
            {
                // Note: if we want to support binding to HasValue and/or Value 
                // properties of nullable types, we need a way to find out if 
                // the rawvalue is Nullable and pass that information here.
 
                DrillIn drillIn = SVI[k].drillIn;
                ICollectionView view = null;

                // first look for info on the parent 
                if (drillIn != DrillIn.Always)
                { 
                    GetInfo(k, parent, ref svs); 
                }
 
                // if that fails, look for information on the view itself
                if (svs.info == null)
                {
                    view = CollectionViewSource.GetDefaultCollectionView(parent, TreeContext); 

                    if (view != null && drillIn != DrillIn.Always) 
                    { 
                        if (view != parent)             // don't duplicate work
                            GetInfo(k, view, ref svs); 
                    }
                }

                // if that fails, drill in to the current item 
                if (svs.info == null && drillIn != DrillIn.Never && view != null)
                { 
                    newO = view.CurrentItem; 
                    if (newO != null)
                    { 
                        GetInfo(k, newO, ref svs);
                        svs.collectionView = view;
                    }
                    else 
                    {
                        // no current item: use previous info (if known) 
                        svs = _arySVS[k]; 
                        svs.collectionView = view;
                        svs.item = BindingExpression.ReplaceReference(svs.item, BindingExpression.NullDataItem); 
                        if (svs.info == null)
                            svs.info = DependencyProperty.UnsetValue;
                    }
                } 
            }
 
            // update info about new item 
            if (svs.info == null)
            { 
                svs.item = BindingExpression.ReplaceReference(svs.item, BindingExpression.NullDataItem);
                _arySVS[k] = svs;
                _status = PropertyPathStatus.PathError;
                ReportNoInfoError(k, parent); 
                return;
            } 
 
            _arySVS[k] = svs;
            newO = BindingExpression.GetReference(svs.item); 

            if (isExtendedTraceEnabled)
            {
                TraceData.Trace(TraceEventType.Warning, 
                                    TraceData.ReplaceItemLong(
                                        TraceData.Identify(_host.ParentBindingExpression), 
                                        k, 
                                        TraceData.Identify(newO),
                                        TraceData.IdentifyAccessor(svs.info))); 
            }

            // start listening to new item
            if (IsDynamic && SVI[k].type != SourceValueType.Direct) 
            {
                Engine.RegisterForCacheChanges(newO, svs.info); 
 
                INotifyPropertyChanged newPC = newO as INotifyPropertyChanged;
                if (newPC != null) 
                {
                    PropertyChangedEventManager.AddListener(newPC, this, SVI[k].propertyName);
                }
                else 
                {
                    PropertyDescriptor newDesc = svs.info as PropertyDescriptor; 
                    if (newDesc != null && newO != null) 
                        ValueChangedEventManager.AddListener(newO, this, newDesc);
                } 
            }

            // at the last level, set up the default transformer
            if (_host != null && k == Length-1) 
            {
                _host.SetupDefaultValueConverter(svs.type); 
 
                // also, check for request to update a read-only property
                if (_host.IsReflective) 
                {
                    CheckReadOnly(newO, svs.info);
                }
            } 
        }
 
        void ReportNoInfoError(int k, object parent) 
        {
            // report cannot find info.  Ignore when in priority bindings. 
            if (TraceData.IsEnabled)
            {
                BindingExpression bindingExpression = (_host != null) ? _host.ParentBindingExpression : null;
                if (bindingExpression == null || !bindingExpression.IsInPriorityBindingExpression) 
                {
                    SourceValueInfo svi = SVI[k]; 
                    string cs = (svi.type != SourceValueType.Indexer) ? svi.name : "[" + svi.name + "]"; 
                    string ps = TraceData.DescribeSourceObject(parent);
                    string os = (svi.drillIn == DrillIn.Always) ? "current item of collection" : "object"; 

                    // if the parent is null, the path error probably only means the
                    // data provider hasn't produced any data yet.  When it does,
                    // the binding will try again and probably succeed.  Give milder 
                    // feedback for this special case, so as not to alarm users unduly.
                    if (parent == null) 
                    { 
                        TraceData.Trace(TraceEventType.Information, TraceData.NullItem(cs, os), bindingExpression);
                    } 
                    // Similarly, if the parent is the NewItemPlaceholder.
                    else if (parent == CollectionView.NewItemPlaceholder)
                    {
                        TraceData.Trace(TraceEventType.Information, TraceData.PlaceholderItem(cs, os), bindingExpression); 
                    }
                    else 
                    { 
                        TraceEventType traceType = (bindingExpression != null) ? bindingExpression.TraceLevel : TraceEventType.Error;
                        TraceData.Trace(traceType, TraceData.ClrReplaceItem(cs, ps, os), bindingExpression); 
                    }
                }
            }
        } 

        // look for property/indexer on the given item 
        private void GetInfo(int k, object item, ref SourceValueState svs) 
        {
#if DEBUG 
            bool checkCacheResult = false;
#endif
            object oldItem = BindingExpression.GetReference(_arySVS[k].item);
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.GetInfo); 

            // optimization - only change info if the type changed 
            // exception - if the info is a PropertyDescriptor, it might depend 
            // on the item itself (not just the type), so we have to re-fetch
            Type oldType = (oldItem != null) ? oldItem.GetType() : null; 
            Type newType = (item != null) ? item.GetType() : null;
            Type sourceType = null;

            if (newType == oldType && oldItem != BindingExpression.NullDataItem && 
                !(_arySVS[k].info is PropertyDescriptor))
            { 
                svs = _arySVS[k]; 
                svs.item = BindingExpression.ReplaceReference(svs.item, item);
 
                if (isExtendedTraceEnabled)
                {
                    TraceData.Trace(TraceEventType.Warning,
                                        TraceData.GetInfo_Reuse( 
                                            TraceData.Identify(_host.ParentBindingExpression),
                                            k, 
                                            TraceData.IdentifyAccessor(svs.info))); 
                }
                return; 
            }

            // if the new item is null, we won't find a property/indexer on it
            if (newType == null && SVI[k].type != SourceValueType.Direct) 
            {
                svs.info = null; 
                svs.args = null; 
                svs.type = null;
                svs.item = BindingExpression.ReplaceReference(svs.item, item); 

                if (isExtendedTraceEnabled)
                {
                    TraceData.Trace(TraceEventType.Warning, 
                                        TraceData.GetInfo_Null(
                                            TraceData.Identify(_host.ParentBindingExpression), 
                                            k)); 
                }
                return; 
            }

            // optimization - see if we've cached the answer
            int index; 
            bool cacheAccessor = !PropertyPath.IsParameterIndex(SVI[k].name, out index);
            if (cacheAccessor) 
            { 
                AccessorInfo accessorInfo = Engine.AccessorTable[SVI[k].type, newType, SVI[k].name];
                if (accessorInfo != null) 
                {
                    svs.info = accessorInfo.Accessor;
                    svs.type = accessorInfo.PropertyType;
                    svs.args = accessorInfo.Args; 
                    svs.item = BindingExpression.ReplaceReference(svs.item, item);
 
                    if (IsDynamic && SVI[k].type == SourceValueType.Property && svs.info is DependencyProperty) 
                    {
                        _dependencySourcesChanged = true; 
                    }

                    if (isExtendedTraceEnabled)
                    { 
                        TraceData.Trace(TraceEventType.Warning,
                                            TraceData.GetInfo_Cache( 
                                                TraceData.Identify(_host.ParentBindingExpression), 
                                                k,
                                                newType.Name, 
                                                SVI[k].name,
                                                TraceData.IdentifyAccessor(svs.info)));
                    }
 
#if DEBUG   // compute the answer the old-fashioned way, and compare
                    checkCacheResult = true; 
#else 
                    return;
#endif 
                }
            }

            object info = null; 
            object[] args = null;
 
            switch (SVI[k].type) 
            {
            case SourceValueType.Property: 
                info = _parent.ResolvePropertyName(k, item, newType, TreeContext);

                if (isExtendedTraceEnabled)
                { 
                    TraceData.Trace(TraceEventType.Warning,
                                        TraceData.GetInfo_Property( 
                                            TraceData.Identify(_host.ParentBindingExpression), 
                                            k,
                                            newType.Name, 
                                            SVI[k].name,
                                            TraceData.IdentifyAccessor(info)));
                }
 
                DependencyProperty dp;
                PropertyInfo pi1; 
                PropertyDescriptor pd; 
                PropertyPath.DowncastAccessor(info, out dp, out pi1, out pd);
 
                if (dp != null)
                {
                    sourceType = dp.PropertyType;
                    if (IsDynamic) 
                    {
#if DEBUG 
                        if (checkCacheResult) 
                            Debug.Assert(_dependencySourcesChanged, "Cached accessor didn't change sources");
#endif 
                        _dependencySourcesChanged = true;
                    }
                    break;
                } 
                else if (pi1 != null)
                { 
                    sourceType = pi1.PropertyType; 
                }
                if (pd != null) 
                {
                    sourceType = pd.PropertyType;
                }
                break; 

            case SourceValueType.Indexer: 
                IndexerParameterInfo[] aryInfo = _parent.ResolveIndexerParams(k, TreeContext); 

                // Check if we should treat the indexer as a property instead. 
                // (See ShouldConvertIndexerToProperty for why we might do that.)
                if (aryInfo.Length == 1 &&
                    (aryInfo[0].type == null || aryInfo[0].type == typeof(string)))
                { 
                    string name = (string)aryInfo[0].value;
                    if (ShouldConvertIndexerToProperty(item, newType, name)) 
                    { 
                        _parent.ReplaceIndexerByProperty(k, name);
                        goto case SourceValueType.Property; 
                    }
                }

                args = new object[aryInfo.Length]; 

                // find the matching indexer 
                MemberInfo[][] aryMembers= new MemberInfo[][]{ newType.GetDefaultMembers(), null }; 
                if (item is IList)
                    aryMembers[1] = typeof(IList).GetDefaultMembers(); 

                for (int ii=0; info==null && ii 0)
                {
                    Debug.Assert(false,
                        String.Format("Accessor cache returned incorrect result for ({0},{1},{2})\n{3}", 
                            SVI[k].type, newType.Name, SVI[k].name, sb.ToString()));
                } 
 
                return;
            } 
#endif
            svs.info = info;
            svs.args = args;
            svs.type = sourceType; 
            svs.item = BindingExpression.ReplaceReference(svs.item, item);
 
            // cache the answer, to avoid doing all that reflection again 
            // (but not if the answer is a PropertyDescriptor,
            // since then the answer potentially depends on the item itself) 
            if (cacheAccessor && info != null && !(info is PropertyDescriptor))
            {
                Engine.AccessorTable[SVI[k].type, newType, SVI[k].name] =
                            new AccessorInfo(info, sourceType, args); 
            }
        } 
 
        // convert the (string) argument names to types appropriate for use with
        // the given property.  Put the results in the args[] array.  Return 
        // true if everything works.
        private bool MatchIndexerParameters(PropertyInfo pi, IndexerParameterInfo[] aryInfo, object[] args)
        {
            ParameterInfo[] aryPI = pi.GetIndexParameters(); 

            // must have the right number of parameters 
            if (aryPI.Length != aryInfo.Length) 
                return false;
 
            // each parameter must be settable from user-specified type or from a string
            for (int i=0; i= _arySVS.Length)
                return DependencyProperty.UnsetValue; 

            object item = BindingExpression.GetReference(_arySVS[k].item);
            object info = _arySVS[k].info;
 
            // try to get the value, unless (a) binding is being detached,
            // (b) no info - e.g. Nullable with no value, or (c) item expected 
            // but not present - e.g. currency moved off the end. 
            if (item != BindingExpression.NullDataItem && info != null && !(item == null && info != DependencyProperty.UnsetValue))
            { 
                object o = DependencyProperty.UnsetValue;
                DependencyProperty dp = info as DependencyProperty;

                // if the binding is async, post a request to get the value 
                if (!(dp != null || SVI[k].type == SourceValueType.Direct))
                { 
                    if (_host != null && _host.AsyncGet(item, k)) 
                    {
                        _status = PropertyPathStatus.AsyncRequestPending; 
                        return AsyncRequestPending;
                    }
                }
 
                // PreSharp uses message numbers that the C# compiler doesn't know about.
                // Disable the C# complaints, per the PreSharp documentation. 
                #pragma warning disable 1634, 1691 

                // PreSharp complains about catching NullReference (and other) exceptions. 
                // It doesn't recognize that IsCriticalException() handles these correctly.
                #pragma warning disable 56500

                try 
                {
                    o = GetValue(item, k); 
                } 
                // Catch all exceptions.  There is no app code on the stack,
                // so the exception isn't actionable by the app. 
                // Yet we don't want to crash the app.
                catch (Exception ex)    // if error getting value, we will use fallback/default instead
                {
                    if (CriticalExceptions.IsCriticalException(ex)) 
                        throw;
                    if (_host != null) 
                        _host.ReportGetValueError(k, item, ex); 
                }
                catch // non CLS compliant exception 
                {
                    if (_host != null)
                        _host.ReportGetValueError(k, item, new InvalidOperationException(SR.Get(SRID.NonCLSException, "GetValue")));
                } 

                #pragma warning restore 56500 
                #pragma warning restore 1634, 1691 

                return o; 
            }

            if (_host != null)
            { 
                _host.ReportRawValueErrors(k, item, info);
            } 
 
            return DependencyProperty.UnsetValue;
        } 

        void SetPropertyInfo(object info, out PropertyInfo pi, out PropertyDescriptor pd, out DependencyProperty dp)
        {
            pi = null; 
            pd = null;
            dp = info as DependencyProperty; 
 
            if (dp == null)
            { 
                pi = info as PropertyInfo;
                if (pi == null)
                    pd = info as PropertyDescriptor;
            } 
        }
 
        void CheckReadOnly(object item, object info) 
        {
            PropertyInfo pi; 
            PropertyDescriptor pd;
            DependencyProperty dp;
            SetPropertyInfo(info, out pi, out pd, out dp);
 
            if (pi != null)
            { 
                if (pi.GetSetMethod() == null) 
                    throw new InvalidOperationException(SR.Get(SRID.CannotWriteToReadOnly, item.GetType(), pi.Name));
            } 
            else if (pd != null)
            {
                if (pd.IsReadOnly)
                    throw new InvalidOperationException(SR.Get(SRID.CannotWriteToReadOnly, item.GetType(), pd.Name)); 
            }
            else if (dp != null) 
            { 
                if (dp.ReadOnly)
                    throw new InvalidOperationException(SR.Get(SRID.CannotWriteToReadOnly, item.GetType(), dp.Name)); 
            }
        }

        // see whether DBNull is a valid value for update, and cache the answer 
        void DetermineWhetherDBNullIsValid()
        { 
            bool result = false; 
            PropertyDescriptor pd;
            object item = GetItem(Length - 1); 

            if (item != null &&
                item.GetType().Namespace.StartsWith("System.Data", StringComparison.Ordinal) &&
                ((pd = GetAccessor(Length-1) as PropertyDescriptor) != null)) 
            {
                result = DetermineWhetherDBNullIsValid(item, pd); 
            } 

            _isDBNullValidForUpdate = result; 
        }

        // separate method, to avoid loading System.Data.dll until necessary
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 
        bool DetermineWhetherDBNullIsValid(object item, PropertyDescriptor pd)
        { 
            System.Data.DataRowView drv = item as System.Data.DataRowView; 
            if (drv == null)
                return false; 

            // this code was provided by the ADO team
            System.Data.DataColumn column = drv.DataView.Table.Columns[pd.Name];
            return (column != null) && column.AllowDBNull; 
        }
 
        ///  
        /// Handle events from the centralized event table
        ///  
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            if (IsExtendedTraceEnabled(TraceDataLevel.Events))
            { 
                TraceData.Trace(TraceEventType.Warning,
                                    TraceData.GotEvent( 
                                        TraceData.Identify(_host.ParentBindingExpression), 
                                        TraceData.IdentifyWeakEvent(managerType),
                                        TraceData.Identify(sender))); 
            }

            if (managerType == typeof(PropertyChangedEventManager))
            { 
                PropertyChangedEventArgs pce = (PropertyChangedEventArgs)e;
                _host.OnSourcePropertyChanged(sender, pce.PropertyName); 
            } 
            else if (managerType == typeof(ValueChangedEventManager))
            { 
                ValueChangedEventArgs vce = (ValueChangedEventArgs)e;
                _host.OnSourcePropertyChanged(sender, vce.PropertyDescriptor.Name);
            }
            else 
            {
                return false;       // unrecognized event 
            } 

            return true; 
        }

        bool IsExtendedTraceEnabled(TraceDataLevel level)
        { 
            if (_host != null)
            { 
                return TraceData.IsExtendedTraceEnabled(_host.ParentBindingExpression, level); 
            }
            else 
            {
                return false;
            }
        } 

        //----------------------------------------------------- 
        // 
        //  Private Classes
        // 
        //------------------------------------------------------

        // helper for setting context via the "using" pattern
        class ContextHelper : IDisposable 
        {
            PropertyPathWorker _owner; 
 
            public ContextHelper(PropertyPathWorker owner)
            { 
                _owner = owner;
            }

            public void SetContext(object rootItem) 
            {
                _owner.TreeContext = rootItem as DependencyObject; 
                _owner.AttachToRootItem(rootItem); 
            }
 
            void IDisposable.Dispose()
            {
                _owner.DetachFromRootItem();
                _owner.TreeContext = null; 
            }
        } 
 
        //-----------------------------------------------------
        // 
        //  Private Enums, Structs, Constants
        //
        //------------------------------------------------------
 
        struct SourceValueState
        { 
            public ICollectionView collectionView; 
            public object item;
            public object info;             // PropertyInfo or PropertyDescriptor or DP 
            public Type type;               // Type of the value (useful for Arrays)
            public object[] args;           // for indexers
        }
 
        static readonly Char[] s_comma = new Char[]{','};
        static readonly Char[] s_dot   = new Char[]{'.'}; 
 
        static readonly object NoParent = new object();
        static readonly object AsyncRequestPending = new object(); 

        //------------------------------------------------------
        //
        //  Private Fields 
        //
        //----------------------------------------------------- 
 
        PropertyPath        _parent;
        PropertyPathStatus  _status; 
        object              _treeContext;
        object              _rootItem;
        SourceValueState[]  _arySVS;
        ContextHelper       _contextHelper; 

        ClrBindingWorker    _host; 
        DataBindEngine      _engine; 

        bool                _dependencySourcesChanged; 
        bool                _isDynamic;
        bool?               _isDBNullValidForUpdate;
    }
 
}
 

// 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 PropertyPathWorker object, workhorse for CLR bindings 
// 
//---------------------------------------------------------------------------
 
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics; 
using System.Reflection;
using System.Globalization; 
using System.Text; 
using System.Windows.Threading;
using System.Windows; 
using System.Windows.Controls;                      // Validation
using System.Windows.Data;
using MS.Internal;
using MS.Internal.Hashing.PresentationFramework;   // HashHelper 

namespace MS.Internal.Data 
{ 
    internal sealed class PropertyPathWorker: IWeakEventListener
    { 
        //-----------------------------------------------------
        //
        //  Constructors
        // 
        //-----------------------------------------------------
 
        internal PropertyPathWorker(PropertyPath path) 
            : this(path, DataBindEngine.CurrentDataBindEngine)
        { 
        }

        internal PropertyPathWorker(PropertyPath path, ClrBindingWorker host, bool isDynamic, DataBindEngine engine)
            : this(path, engine) 
        {
            _host = host; 
            _isDynamic = isDynamic; 
        }
 
        private PropertyPathWorker(PropertyPath path, DataBindEngine engine)
        {
            _parent = path;
            _arySVS = new SourceValueState[path.Length]; 
            _engine = engine;
 
            // initialize each level to NullDataItem, so that the first real 
            // item will force a change
            for (int i=_arySVS.Length-1; i>=0; --i) 
            {
                _arySVS[i].item = BindingExpression.CreateReference(BindingExpression.NullDataItem);
            }
        } 

        //------------------------------------------------------ 
        // 
        //  Internal Properties
        // 
        //-----------------------------------------------------

        internal int Length { get { return _parent.Length; } }
        internal PropertyPathStatus Status { get { return _status; } } 

        internal DependencyObject TreeContext 
        { 
            get { return BindingExpression.GetReference(_treeContext) as DependencyObject; }
            set { _treeContext = BindingExpression.CreateReference(value); } 
        }

        internal void SetTreeContext(WeakReference wr)
        { 
            _treeContext = BindingExpression.CreateReference(wr);
        } 
 
        internal bool IsDBNullValidForUpdate
        { 
            get
            {
                if (!_isDBNullValidForUpdate.HasValue)
                { 
                    DetermineWhetherDBNullIsValid();
                } 
 
                return _isDBNullValidForUpdate.Value;
            } 
        }

        internal object SourceItem
        { 
            get
            { 
                int level = Length-1; 
                return (level >= 0) ? GetItem(level) : null;
            } 
        }

        internal string SourcePropertyName
        { 
            get
            { 
                DependencyProperty dp; 
                PropertyInfo pi;
                PropertyDescriptor pd; 
                int level = Length-1;

                if (level < 0)
                    return null; 

                SetPropertyInfo(GetAccessor(level), out pi, out pd, out dp); 
 
                return  (dp != null) ? dp.Name :
                        (pi != null) ? pi.Name : 
                        (pd != null) ? pd.Name : null;
            }
        }
 
        //------------------------------------------------------
        // 
        //  Internal Methods 
        //
        //------------------------------------------------------ 

        //-------  common methods ------

        internal object GetItem(int level) 
        {
            return BindingExpression.GetReference(_arySVS[level].item); 
        } 

        internal object GetAccessor(int level) 
        {
            return _arySVS[level].info;
        }
 
        internal object[] GetIndexerArguments(int level)
        { 
            return _arySVS[level].args; 
        }
 
        internal Type GetType(int level)
        {
            return _arySVS[level].type;
        } 

        //-------  target mode ------ 
 
        // Set the context for the path.  Use this method in "target" mode
        // to connect the path to a rootItem for a short time: 
        //      using (path.SetContext(myItem))
        //      {
        //          ... call target-mode convenience methods ...
        //      } 
        internal IDisposable SetContext(object rootItem)
        { 
            if (_contextHelper == null) 
                _contextHelper = new ContextHelper(this);
 
            _contextHelper.SetContext(rootItem);
            return _contextHelper;
        }
 
        //-------  source mode (should only be called by ClrBindingWorker) ------
 
        internal void AttachToRootItem(object rootItem) 
        {
            _rootItem = BindingExpression.CreateReference(rootItem); 
            UpdateSourceValueState(-1, null);
        }

        internal void DetachFromRootItem() 
        {
            _rootItem = BindingExpression.NullDataItem; 
            UpdateSourceValueState(-1, null); 
            _rootItem = null;
        } 

        internal object GetValue(object item, int level)
        {
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.GetValue); 
            DependencyProperty dp;
            PropertyInfo pi; 
            PropertyDescriptor pd; 
            object value = DependencyProperty.UnsetValue;
            SetPropertyInfo(_arySVS[level].info, out pi, out pd, out dp); 

            switch (SVI[level].type)
            {
            case SourceValueType.Property: 
                if (pi != null)
                { 
                    value = pi.GetValue(item, null); 
                }
                else if (pd != null) 
                {
                    bool indexerIsNext = (level+1 < SVI.Length && SVI[level+1].type == SourceValueType.Indexer);
                    value = Engine.GetValue(item, pd, indexerIsNext);
                } 
                else if (dp != null)
                { 
                    DependencyObject d = (DependencyObject)item; 
                    if (level != Length-1 || _host == null || _host.TransfersDefaultValue)
                        value = d.GetValue(dp); 
                    else if (!Helper.HasDefaultValue(dp, d, d as FrameworkElement, d as FrameworkContentElement))
                        value = d.GetValue(dp);
                    else
                        value = BindingExpression.IgnoreDefaultValue; 
                }
                break; 
 
            case SourceValueType.Indexer:
                // 
                if (pi != null)
                {
                    value = pi.GetValue(item,
                                    BindingFlags.GetProperty, null, 
                                    _arySVS[level].args,
                                    CultureInfo.InvariantCulture); 
                } 
                else
                { 
                    throw new NotSupportedException(SR.Get(SRID.IndexedPropDescNotImplemented));
                }
                break;
 
            case SourceValueType.Direct:
                value = item; 
                break; 
            }
 
            if (isExtendedTraceEnabled)
            {
                object accessor = _arySVS[level].info;
                if (accessor == DependencyProperty.UnsetValue) 
                    accessor = null;
 
                TraceData.Trace(TraceEventType.Warning, 
                                    TraceData.GetValue(
                                        TraceData.Identify(_host.ParentBindingExpression), 
                                        level,
                                        TraceData.Identify(item),
                                        TraceData.IdentifyAccessor(accessor),
                                        TraceData.Identify(value))); 
            }
 
            return value; 
        }
 
        internal void SetValue(object item, object value)
        {
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.GetValue);
            PropertyInfo pi; 
            PropertyDescriptor pd;
            DependencyProperty dp; 
            int level = _arySVS.Length - 1; 
            SetPropertyInfo(_arySVS[level].info, out pi, out pd, out dp);
 
            if (isExtendedTraceEnabled)
            {
                TraceData.Trace(TraceEventType.Warning,
                                    TraceData.SetValue( 
                                        TraceData.Identify(_host.ParentBindingExpression),
                                        level, 
                                        TraceData.Identify(item), 
                                        TraceData.IdentifyAccessor(_arySVS[level].info),
                                        TraceData.Identify(value))); 
            }

            switch (SVI[level].type)
            { 
            case SourceValueType.Property:
                if (pd != null) 
                { 
                    pd.SetValue(item, value);
                } 
                else if (pi != null)
                {
                    pi.SetValue(item, value, null);
                } 
                else if (dp != null)
                { 
                    ((DependencyObject)item).SetValue(dp, value); 
                }
                break; 

            case SourceValueType.Indexer:
                //
                if (pi != null) 
                {
                    pi.SetValue(item, value, 
                                    BindingFlags.SetProperty, null, 
                                    _arySVS[level].args,
                                    CultureInfo.InvariantCulture); 
                }
                else
                {
                    throw new NotSupportedException(SR.Get(SRID.IndexedPropDescNotImplemented)); 
                }
                break; 
            } 
        }
 
        internal object RawValue()
        {
            object rawValue = RawValue(Length-1);
 
            if (rawValue == AsyncRequestPending)
                rawValue = DependencyProperty.UnsetValue;     // the real value will arrive later 
 
            return rawValue;
        } 

        // Called by BE.UpdateTarget().  Re-fetch the value at each level.
        // If there's a difference, simulate a property-change at that level.
        internal void RefreshValue() 
        {
            for (int k=1; k<_arySVS.Length; ++k) 
            { 
                object oldValue = BindingExpression.GetReference(_arySVS[k].item);
                if (!Object.Equals(oldValue, RawValue(k-1))) 
                {
                    UpdateSourceValueState(k-1, null);
                    return;
                } 
            }
 
            UpdateSourceValueState(Length-1, null); 
        }
 
        // return the source level where the change happened, or -1 if the
        // change is irrelevant.
        internal int LevelForPropertyChange(object item, string propertyName)
        { 
            // This test must be thread-safe - it can get called on the "wrong" context.
            // It's read-only (good).  And if another thread changes the values it reads, 
            // the worst that can happen is to schedule a transfer operation needlessly - 
            // the operation itself won't do anything (since the test is repeated on the
            // right thread). 

            bool isIndexer = propertyName == Binding.IndexerName;

            for (int k=0; k<_arySVS.Length; ++k) 
            {
                if (BindingExpression.GetReference(_arySVS[k].item) == item && 
                        (String.IsNullOrEmpty(propertyName) || 
                         (isIndexer && SVI[k].type == MS.Internal.Data.SourceValueType.Indexer) ||
                         String.Equals(SVI[k].propertyName, propertyName, StringComparison.OrdinalIgnoreCase))) 
                {
                    return k;
                }
            } 

            return -1; 
        } 

        internal void OnPropertyChangedAtLevel(int level) 
        {
            UpdateSourceValueState(level, null);
        }
 
        internal void OnCurrentChanged(ICollectionView collectionView)
        { 
            for (int k=0; k= 0 && SVI[level].type == SourceValueType.Property)
            { 
                object item = GetItem(level);
                IDataErrorInfo idei = item as IDataErrorInfo;
                if (idei != null)
                { 
                    DependencyProperty dp;
                    PropertyInfo pi; 
                    PropertyDescriptor pd; 
                    SetPropertyInfo(GetAccessor(level), out pi, out pd, out dp);
 
                    string name =   (dp != null) ? dp.Name :
                                    (pi != null) ? pi.Name :
                                    (pd != null) ? pd.Name : null;
                    string error; 

                    if (name != null) 
                    { 
                        // get the data error information, if any, by calling idie[name].
                        // We do this in a paranoid way, even though indexers with 
                        // string-valued arguments are not supposed to throw exceptions.

                        // PreSharp uses message numbers that the C# compiler doesn't know about.
                        // Disable the C# complaints, per the PreSharp documentation. 
                        #pragma warning disable 1634, 1691
 
                        // PreSharp complains about catching NullReference (and other) exceptions. 
                        // It doesn't recognize that IsCriticalException() handles these correctly.
                        #pragma warning disable 56500 

                        try
                        {
                            error = idei[name]; 
                        }
                        catch (Exception ex) 
                        { 
                            if (CriticalExceptions.IsCriticalException(ex))
                                throw; 

                            error = null;

                            if (TraceData.IsEnabled) 
                            {
                                TraceData.Trace(TraceEventType.Error, 
                                                TraceData.DataErrorInfoFailed( 
                                                    name,
                                                    item.GetType().FullName, 
                                                    ex.GetType().FullName,
                                                    ex.Message),
                                                bindingExpressionBase);
                            } 
                        }
                        #pragma warning restore 56500 
                        #pragma warning restore 1634, 1691 
                    }
                    else 
                    {
                        error = null;
                    }
 
                    if (!String.IsNullOrEmpty(error))
                    { 
                        result = new ValidationError(DataErrorValidationRule.Instance, 
                                                    bindingExpressionBase,
                                                    error, 
                                                    null);
                    }
                }
            } 

            return result; 
        } 

        //----------------------------------------------------- 
        //
        //  Private Properties
        //
        //------------------------------------------------------ 

        bool IsDynamic { get { return _isDynamic; } } 
        SourceValueInfo[] SVI { get { return _parent.SVI; } } 
        DataBindEngine Engine { get { return _engine; } }
 
        //-----------------------------------------------------
        //
        //  Private Methods
        // 
        //-----------------------------------------------------
 
        // fill in the SourceValueState with updated infomation, starting at level k+1. 
        // If view isn't null, also update the current item at level k.
        private void UpdateSourceValueState(int k, ICollectionView collectionView) 
        {
            UpdateSourceValueState(k, collectionView, BindingExpression.NullDataItem, false);
        }
 
        // fill in the SourceValueState with updated infomation, starting at level k+1.
        // If view isn't null, also update the current item at level k. 
        private void UpdateSourceValueState(int k, ICollectionView collectionView, object newValue, bool isASubPropertyChange) 
        {
            // give host a chance to shut down the binding if the target has 
            // gone away
            DependencyObject target = null;
            if (_host != null)
            { 
                target = _host.CheckTarget();
                if (_rootItem != BindingExpression.NullDataItem && target == null) 
                    return; 
            }
 
            int initialLevel = k;
            object rawValue = null;

            // optimistically assume the new value will fix previous path errors 
            _status = PropertyPathStatus.Active;
 
            // prepare to collect changes to dependency sources 
            _dependencySourcesChanged = false;
 
            // Update the current item at level k, if requested
            if (collectionView != null)
            {
                Debug.Assert(0<=k && k<_arySVS.Length && _arySVS[k].collectionView == collectionView, "bad parameters to UpdateSourceValueState"); 
                ReplaceItem(k, collectionView.CurrentItem, NoParent);
            } 
 
            // update the remaining levels
            for (++k; k<_arySVS.Length; ++k) 
            {
                isASubPropertyChange = false;   // sub-property changes only matter at the last level

                ICollectionView oldCollectionView = _arySVS[k].collectionView; 

                // replace the item at level k using parent from level k-1 
                rawValue = (newValue == BindingExpression.NullDataItem) ? RawValue(k-1) : newValue; 
                newValue = BindingExpression.NullDataItem;
                if (rawValue == AsyncRequestPending) 
                {
                    _status = PropertyPathStatus.AsyncRequestPending;
                    break;      // we'll resume the loop after the request completes
                } 

                ReplaceItem(k, BindingExpression.NullDataItem, rawValue); 
 
                // replace view, if necessary
                ICollectionView newCollectionView = _arySVS[k].collectionView; 
                if (oldCollectionView != newCollectionView && _host != null)
                {
                    _host.ReplaceCurrentItem(oldCollectionView, newCollectionView);
                } 
            }
 
            // notify binding about what happened 
            if (_host != null)
            { 
                _host.NewValueAvailable(_dependencySourcesChanged, initialLevel < 0, isASubPropertyChange);
            }

            GC.KeepAlive(target);   // keep target alive during changes (bug 956831) 
        }
 
        // replace the item at level k with the given item, or with an item obtained from the given parent 
        private void ReplaceItem(int k, object newO, object parent)
        { 
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.ReplaceItem);
            SourceValueState svs = new SourceValueState();

            object oldO = BindingExpression.GetReference(_arySVS[k].item); 

            // stop listening to old item 
            if (IsDynamic && SVI[k].type != SourceValueType.Direct) 
            {
                INotifyPropertyChanged oldPC = oldO as INotifyPropertyChanged; 
                if (oldPC != null)
                {
                    PropertyChangedEventManager.RemoveListener(oldPC, this, SVI[k].propertyName);
                } 
                else
                { 
                    PropertyDescriptor oldDesc = _arySVS[k].info as PropertyDescriptor; 
                    if (oldDesc != null && oldO != null && oldO != BindingExpression.NullDataItem)
                    { 
                        ValueChangedEventManager.RemoveListener(oldO, this, oldDesc);
                    }
                }
 
                DependencyProperty dp = _arySVS[k].info as DependencyProperty;
                if (dp != null) 
                { 
                    _dependencySourcesChanged = true;
                } 
            }

            // clear the IsDBNullValid cache
            _isDBNullValidForUpdate = null; 

            if (newO == null || parent == DependencyProperty.UnsetValue || parent == BindingExpression.NullDataItem) 
            { 
                _arySVS[k].item = BindingExpression.ReplaceReference(_arySVS[k].item, newO);
 
                if (parent == DependencyProperty.UnsetValue || parent == BindingExpression.NullDataItem)
                    _arySVS[k].collectionView = null;

                if (isExtendedTraceEnabled) 
                {
                    TraceData.Trace(TraceEventType.Warning, 
                                        TraceData.ReplaceItemShort( 
                                            TraceData.Identify(_host.ParentBindingExpression),
                                            k, 
                                            TraceData.Identify(newO)));
                }

                return; 
            }
 
            // obtain the new item and its access info 
            if (newO != BindingExpression.NullDataItem)
            { 
                GetInfo(k, newO, ref svs);
                svs.collectionView = _arySVS[k].collectionView;
            }
            else 
            {
                // Note: if we want to support binding to HasValue and/or Value 
                // properties of nullable types, we need a way to find out if 
                // the rawvalue is Nullable and pass that information here.
 
                DrillIn drillIn = SVI[k].drillIn;
                ICollectionView view = null;

                // first look for info on the parent 
                if (drillIn != DrillIn.Always)
                { 
                    GetInfo(k, parent, ref svs); 
                }
 
                // if that fails, look for information on the view itself
                if (svs.info == null)
                {
                    view = CollectionViewSource.GetDefaultCollectionView(parent, TreeContext); 

                    if (view != null && drillIn != DrillIn.Always) 
                    { 
                        if (view != parent)             // don't duplicate work
                            GetInfo(k, view, ref svs); 
                    }
                }

                // if that fails, drill in to the current item 
                if (svs.info == null && drillIn != DrillIn.Never && view != null)
                { 
                    newO = view.CurrentItem; 
                    if (newO != null)
                    { 
                        GetInfo(k, newO, ref svs);
                        svs.collectionView = view;
                    }
                    else 
                    {
                        // no current item: use previous info (if known) 
                        svs = _arySVS[k]; 
                        svs.collectionView = view;
                        svs.item = BindingExpression.ReplaceReference(svs.item, BindingExpression.NullDataItem); 
                        if (svs.info == null)
                            svs.info = DependencyProperty.UnsetValue;
                    }
                } 
            }
 
            // update info about new item 
            if (svs.info == null)
            { 
                svs.item = BindingExpression.ReplaceReference(svs.item, BindingExpression.NullDataItem);
                _arySVS[k] = svs;
                _status = PropertyPathStatus.PathError;
                ReportNoInfoError(k, parent); 
                return;
            } 
 
            _arySVS[k] = svs;
            newO = BindingExpression.GetReference(svs.item); 

            if (isExtendedTraceEnabled)
            {
                TraceData.Trace(TraceEventType.Warning, 
                                    TraceData.ReplaceItemLong(
                                        TraceData.Identify(_host.ParentBindingExpression), 
                                        k, 
                                        TraceData.Identify(newO),
                                        TraceData.IdentifyAccessor(svs.info))); 
            }

            // start listening to new item
            if (IsDynamic && SVI[k].type != SourceValueType.Direct) 
            {
                Engine.RegisterForCacheChanges(newO, svs.info); 
 
                INotifyPropertyChanged newPC = newO as INotifyPropertyChanged;
                if (newPC != null) 
                {
                    PropertyChangedEventManager.AddListener(newPC, this, SVI[k].propertyName);
                }
                else 
                {
                    PropertyDescriptor newDesc = svs.info as PropertyDescriptor; 
                    if (newDesc != null && newO != null) 
                        ValueChangedEventManager.AddListener(newO, this, newDesc);
                } 
            }

            // at the last level, set up the default transformer
            if (_host != null && k == Length-1) 
            {
                _host.SetupDefaultValueConverter(svs.type); 
 
                // also, check for request to update a read-only property
                if (_host.IsReflective) 
                {
                    CheckReadOnly(newO, svs.info);
                }
            } 
        }
 
        void ReportNoInfoError(int k, object parent) 
        {
            // report cannot find info.  Ignore when in priority bindings. 
            if (TraceData.IsEnabled)
            {
                BindingExpression bindingExpression = (_host != null) ? _host.ParentBindingExpression : null;
                if (bindingExpression == null || !bindingExpression.IsInPriorityBindingExpression) 
                {
                    SourceValueInfo svi = SVI[k]; 
                    string cs = (svi.type != SourceValueType.Indexer) ? svi.name : "[" + svi.name + "]"; 
                    string ps = TraceData.DescribeSourceObject(parent);
                    string os = (svi.drillIn == DrillIn.Always) ? "current item of collection" : "object"; 

                    // if the parent is null, the path error probably only means the
                    // data provider hasn't produced any data yet.  When it does,
                    // the binding will try again and probably succeed.  Give milder 
                    // feedback for this special case, so as not to alarm users unduly.
                    if (parent == null) 
                    { 
                        TraceData.Trace(TraceEventType.Information, TraceData.NullItem(cs, os), bindingExpression);
                    } 
                    // Similarly, if the parent is the NewItemPlaceholder.
                    else if (parent == CollectionView.NewItemPlaceholder)
                    {
                        TraceData.Trace(TraceEventType.Information, TraceData.PlaceholderItem(cs, os), bindingExpression); 
                    }
                    else 
                    { 
                        TraceEventType traceType = (bindingExpression != null) ? bindingExpression.TraceLevel : TraceEventType.Error;
                        TraceData.Trace(traceType, TraceData.ClrReplaceItem(cs, ps, os), bindingExpression); 
                    }
                }
            }
        } 

        // look for property/indexer on the given item 
        private void GetInfo(int k, object item, ref SourceValueState svs) 
        {
#if DEBUG 
            bool checkCacheResult = false;
#endif
            object oldItem = BindingExpression.GetReference(_arySVS[k].item);
            bool isExtendedTraceEnabled = IsExtendedTraceEnabled(TraceDataLevel.GetInfo); 

            // optimization - only change info if the type changed 
            // exception - if the info is a PropertyDescriptor, it might depend 
            // on the item itself (not just the type), so we have to re-fetch
            Type oldType = (oldItem != null) ? oldItem.GetType() : null; 
            Type newType = (item != null) ? item.GetType() : null;
            Type sourceType = null;

            if (newType == oldType && oldItem != BindingExpression.NullDataItem && 
                !(_arySVS[k].info is PropertyDescriptor))
            { 
                svs = _arySVS[k]; 
                svs.item = BindingExpression.ReplaceReference(svs.item, item);
 
                if (isExtendedTraceEnabled)
                {
                    TraceData.Trace(TraceEventType.Warning,
                                        TraceData.GetInfo_Reuse( 
                                            TraceData.Identify(_host.ParentBindingExpression),
                                            k, 
                                            TraceData.IdentifyAccessor(svs.info))); 
                }
                return; 
            }

            // if the new item is null, we won't find a property/indexer on it
            if (newType == null && SVI[k].type != SourceValueType.Direct) 
            {
                svs.info = null; 
                svs.args = null; 
                svs.type = null;
                svs.item = BindingExpression.ReplaceReference(svs.item, item); 

                if (isExtendedTraceEnabled)
                {
                    TraceData.Trace(TraceEventType.Warning, 
                                        TraceData.GetInfo_Null(
                                            TraceData.Identify(_host.ParentBindingExpression), 
                                            k)); 
                }
                return; 
            }

            // optimization - see if we've cached the answer
            int index; 
            bool cacheAccessor = !PropertyPath.IsParameterIndex(SVI[k].name, out index);
            if (cacheAccessor) 
            { 
                AccessorInfo accessorInfo = Engine.AccessorTable[SVI[k].type, newType, SVI[k].name];
                if (accessorInfo != null) 
                {
                    svs.info = accessorInfo.Accessor;
                    svs.type = accessorInfo.PropertyType;
                    svs.args = accessorInfo.Args; 
                    svs.item = BindingExpression.ReplaceReference(svs.item, item);
 
                    if (IsDynamic && SVI[k].type == SourceValueType.Property && svs.info is DependencyProperty) 
                    {
                        _dependencySourcesChanged = true; 
                    }

                    if (isExtendedTraceEnabled)
                    { 
                        TraceData.Trace(TraceEventType.Warning,
                                            TraceData.GetInfo_Cache( 
                                                TraceData.Identify(_host.ParentBindingExpression), 
                                                k,
                                                newType.Name, 
                                                SVI[k].name,
                                                TraceData.IdentifyAccessor(svs.info)));
                    }
 
#if DEBUG   // compute the answer the old-fashioned way, and compare
                    checkCacheResult = true; 
#else 
                    return;
#endif 
                }
            }

            object info = null; 
            object[] args = null;
 
            switch (SVI[k].type) 
            {
            case SourceValueType.Property: 
                info = _parent.ResolvePropertyName(k, item, newType, TreeContext);

                if (isExtendedTraceEnabled)
                { 
                    TraceData.Trace(TraceEventType.Warning,
                                        TraceData.GetInfo_Property( 
                                            TraceData.Identify(_host.ParentBindingExpression), 
                                            k,
                                            newType.Name, 
                                            SVI[k].name,
                                            TraceData.IdentifyAccessor(info)));
                }
 
                DependencyProperty dp;
                PropertyInfo pi1; 
                PropertyDescriptor pd; 
                PropertyPath.DowncastAccessor(info, out dp, out pi1, out pd);
 
                if (dp != null)
                {
                    sourceType = dp.PropertyType;
                    if (IsDynamic) 
                    {
#if DEBUG 
                        if (checkCacheResult) 
                            Debug.Assert(_dependencySourcesChanged, "Cached accessor didn't change sources");
#endif 
                        _dependencySourcesChanged = true;
                    }
                    break;
                } 
                else if (pi1 != null)
                { 
                    sourceType = pi1.PropertyType; 
                }
                if (pd != null) 
                {
                    sourceType = pd.PropertyType;
                }
                break; 

            case SourceValueType.Indexer: 
                IndexerParameterInfo[] aryInfo = _parent.ResolveIndexerParams(k, TreeContext); 

                // Check if we should treat the indexer as a property instead. 
                // (See ShouldConvertIndexerToProperty for why we might do that.)
                if (aryInfo.Length == 1 &&
                    (aryInfo[0].type == null || aryInfo[0].type == typeof(string)))
                { 
                    string name = (string)aryInfo[0].value;
                    if (ShouldConvertIndexerToProperty(item, newType, name)) 
                    { 
                        _parent.ReplaceIndexerByProperty(k, name);
                        goto case SourceValueType.Property; 
                    }
                }

                args = new object[aryInfo.Length]; 

                // find the matching indexer 
                MemberInfo[][] aryMembers= new MemberInfo[][]{ newType.GetDefaultMembers(), null }; 
                if (item is IList)
                    aryMembers[1] = typeof(IList).GetDefaultMembers(); 

                for (int ii=0; info==null && ii 0)
                {
                    Debug.Assert(false,
                        String.Format("Accessor cache returned incorrect result for ({0},{1},{2})\n{3}", 
                            SVI[k].type, newType.Name, SVI[k].name, sb.ToString()));
                } 
 
                return;
            } 
#endif
            svs.info = info;
            svs.args = args;
            svs.type = sourceType; 
            svs.item = BindingExpression.ReplaceReference(svs.item, item);
 
            // cache the answer, to avoid doing all that reflection again 
            // (but not if the answer is a PropertyDescriptor,
            // since then the answer potentially depends on the item itself) 
            if (cacheAccessor && info != null && !(info is PropertyDescriptor))
            {
                Engine.AccessorTable[SVI[k].type, newType, SVI[k].name] =
                            new AccessorInfo(info, sourceType, args); 
            }
        } 
 
        // convert the (string) argument names to types appropriate for use with
        // the given property.  Put the results in the args[] array.  Return 
        // true if everything works.
        private bool MatchIndexerParameters(PropertyInfo pi, IndexerParameterInfo[] aryInfo, object[] args)
        {
            ParameterInfo[] aryPI = pi.GetIndexParameters(); 

            // must have the right number of parameters 
            if (aryPI.Length != aryInfo.Length) 
                return false;
 
            // each parameter must be settable from user-specified type or from a string
            for (int i=0; i= _arySVS.Length)
                return DependencyProperty.UnsetValue; 

            object item = BindingExpression.GetReference(_arySVS[k].item);
            object info = _arySVS[k].info;
 
            // try to get the value, unless (a) binding is being detached,
            // (b) no info - e.g. Nullable with no value, or (c) item expected 
            // but not present - e.g. currency moved off the end. 
            if (item != BindingExpression.NullDataItem && info != null && !(item == null && info != DependencyProperty.UnsetValue))
            { 
                object o = DependencyProperty.UnsetValue;
                DependencyProperty dp = info as DependencyProperty;

                // if the binding is async, post a request to get the value 
                if (!(dp != null || SVI[k].type == SourceValueType.Direct))
                { 
                    if (_host != null && _host.AsyncGet(item, k)) 
                    {
                        _status = PropertyPathStatus.AsyncRequestPending; 
                        return AsyncRequestPending;
                    }
                }
 
                // PreSharp uses message numbers that the C# compiler doesn't know about.
                // Disable the C# complaints, per the PreSharp documentation. 
                #pragma warning disable 1634, 1691 

                // PreSharp complains about catching NullReference (and other) exceptions. 
                // It doesn't recognize that IsCriticalException() handles these correctly.
                #pragma warning disable 56500

                try 
                {
                    o = GetValue(item, k); 
                } 
                // Catch all exceptions.  There is no app code on the stack,
                // so the exception isn't actionable by the app. 
                // Yet we don't want to crash the app.
                catch (Exception ex)    // if error getting value, we will use fallback/default instead
                {
                    if (CriticalExceptions.IsCriticalException(ex)) 
                        throw;
                    if (_host != null) 
                        _host.ReportGetValueError(k, item, ex); 
                }
                catch // non CLS compliant exception 
                {
                    if (_host != null)
                        _host.ReportGetValueError(k, item, new InvalidOperationException(SR.Get(SRID.NonCLSException, "GetValue")));
                } 

                #pragma warning restore 56500 
                #pragma warning restore 1634, 1691 

                return o; 
            }

            if (_host != null)
            { 
                _host.ReportRawValueErrors(k, item, info);
            } 
 
            return DependencyProperty.UnsetValue;
        } 

        void SetPropertyInfo(object info, out PropertyInfo pi, out PropertyDescriptor pd, out DependencyProperty dp)
        {
            pi = null; 
            pd = null;
            dp = info as DependencyProperty; 
 
            if (dp == null)
            { 
                pi = info as PropertyInfo;
                if (pi == null)
                    pd = info as PropertyDescriptor;
            } 
        }
 
        void CheckReadOnly(object item, object info) 
        {
            PropertyInfo pi; 
            PropertyDescriptor pd;
            DependencyProperty dp;
            SetPropertyInfo(info, out pi, out pd, out dp);
 
            if (pi != null)
            { 
                if (pi.GetSetMethod() == null) 
                    throw new InvalidOperationException(SR.Get(SRID.CannotWriteToReadOnly, item.GetType(), pi.Name));
            } 
            else if (pd != null)
            {
                if (pd.IsReadOnly)
                    throw new InvalidOperationException(SR.Get(SRID.CannotWriteToReadOnly, item.GetType(), pd.Name)); 
            }
            else if (dp != null) 
            { 
                if (dp.ReadOnly)
                    throw new InvalidOperationException(SR.Get(SRID.CannotWriteToReadOnly, item.GetType(), dp.Name)); 
            }
        }

        // see whether DBNull is a valid value for update, and cache the answer 
        void DetermineWhetherDBNullIsValid()
        { 
            bool result = false; 
            PropertyDescriptor pd;
            object item = GetItem(Length - 1); 

            if (item != null &&
                item.GetType().Namespace.StartsWith("System.Data", StringComparison.Ordinal) &&
                ((pd = GetAccessor(Length-1) as PropertyDescriptor) != null)) 
            {
                result = DetermineWhetherDBNullIsValid(item, pd); 
            } 

            _isDBNullValidForUpdate = result; 
        }

        // separate method, to avoid loading System.Data.dll until necessary
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 
        bool DetermineWhetherDBNullIsValid(object item, PropertyDescriptor pd)
        { 
            System.Data.DataRowView drv = item as System.Data.DataRowView; 
            if (drv == null)
                return false; 

            // this code was provided by the ADO team
            System.Data.DataColumn column = drv.DataView.Table.Columns[pd.Name];
            return (column != null) && column.AllowDBNull; 
        }
 
        ///  
        /// Handle events from the centralized event table
        ///  
        bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
        {
            if (IsExtendedTraceEnabled(TraceDataLevel.Events))
            { 
                TraceData.Trace(TraceEventType.Warning,
                                    TraceData.GotEvent( 
                                        TraceData.Identify(_host.ParentBindingExpression), 
                                        TraceData.IdentifyWeakEvent(managerType),
                                        TraceData.Identify(sender))); 
            }

            if (managerType == typeof(PropertyChangedEventManager))
            { 
                PropertyChangedEventArgs pce = (PropertyChangedEventArgs)e;
                _host.OnSourcePropertyChanged(sender, pce.PropertyName); 
            } 
            else if (managerType == typeof(ValueChangedEventManager))
            { 
                ValueChangedEventArgs vce = (ValueChangedEventArgs)e;
                _host.OnSourcePropertyChanged(sender, vce.PropertyDescriptor.Name);
            }
            else 
            {
                return false;       // unrecognized event 
            } 

            return true; 
        }

        bool IsExtendedTraceEnabled(TraceDataLevel level)
        { 
            if (_host != null)
            { 
                return TraceData.IsExtendedTraceEnabled(_host.ParentBindingExpression, level); 
            }
            else 
            {
                return false;
            }
        } 

        //----------------------------------------------------- 
        // 
        //  Private Classes
        // 
        //------------------------------------------------------

        // helper for setting context via the "using" pattern
        class ContextHelper : IDisposable 
        {
            PropertyPathWorker _owner; 
 
            public ContextHelper(PropertyPathWorker owner)
            { 
                _owner = owner;
            }

            public void SetContext(object rootItem) 
            {
                _owner.TreeContext = rootItem as DependencyObject; 
                _owner.AttachToRootItem(rootItem); 
            }
 
            void IDisposable.Dispose()
            {
                _owner.DetachFromRootItem();
                _owner.TreeContext = null; 
            }
        } 
 
        //-----------------------------------------------------
        // 
        //  Private Enums, Structs, Constants
        //
        //------------------------------------------------------
 
        struct SourceValueState
        { 
            public ICollectionView collectionView; 
            public object item;
            public object info;             // PropertyInfo or PropertyDescriptor or DP 
            public Type type;               // Type of the value (useful for Arrays)
            public object[] args;           // for indexers
        }
 
        static readonly Char[] s_comma = new Char[]{','};
        static readonly Char[] s_dot   = new Char[]{'.'}; 
 
        static readonly object NoParent = new object();
        static readonly object AsyncRequestPending = new object(); 

        //------------------------------------------------------
        //
        //  Private Fields 
        //
        //----------------------------------------------------- 
 
        PropertyPath        _parent;
        PropertyPathStatus  _status; 
        object              _treeContext;
        object              _rootItem;
        SourceValueState[]  _arySVS;
        ContextHelper       _contextHelper; 

        ClrBindingWorker    _host; 
        DataBindEngine      _engine; 

        bool                _dependencySourcesChanged; 
        bool                _isDynamic;
        bool?               _isDBNullValidForUpdate;
    }
 
}
 

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