PropertyPathWorker.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / Data / PropertyPathWorker.cs / 7 / 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 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); 
                    } 
                    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, true, 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