PropertyPathWorker.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / Data / PropertyPathWorker.cs / 1483137 / 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 System.Windows.Markup;
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;
                object item = (level >= 0) ? GetItem(level) : null; 
                if (item == BindingExpression.NullDataItem)
                {
                    item = null;
                } 

                return item; 
            } 
        }
 
        internal string SourcePropertyName
        {
            get
            { 
                int level = Length-1;
 
                if (level < 0) 
                    return null;
 
                switch (SVI[level].type)
                {
                    case SourceValueType.Property:
                        // return the real name of the property 
                        DependencyProperty dp;
                        PropertyInfo pi; 
                        PropertyDescriptor pd; 
                        DynamicPropertyAccessor dpa;
 
                        SetPropertyInfo(GetAccessor(level), out pi, out pd, out dp, out dpa);
                        return  (dp != null) ? dp.Name :
                                (pi != null) ? pi.Name :
                                (pd != null) ? pd.Name : 
                                (dpa != null) ? dpa.PropertyName : null;
 
                    case SourceValueType.Indexer: 
                        // return the indexer string, e.g. "[foo]"
                        string s = _parent.Path; 
                        int lastBracketIndex = s.LastIndexOf('[');
                        return s.Substring(lastBracketIndex);
                }
 
                // in all other cases, no name is available
                return null; 
            } 
        }
 
        // true when we need to register for direct notification from the RawValue,
        // i.e. when it's a DO that we get to via a non-DP
        internal bool NeedsDirectNotification
        { 
            get { return _needsDirectNotification; }
            private set 
            { 
                if (value)
                { 
                    _dependencySourcesChanged = true;
                }
                _needsDirectNotification = 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) 
        {
            object[] args = _arySVS[level].args; 

            // unwrap the IList wrapper, if any
            IListIndexerArg wrapper;
            if (args != null && args.Length == 1 && 
                (wrapper = args[0] as IListIndexerArg) != null)
            { 
                return new object[] { wrapper.Value }; 
            }
 
            return 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;
            DynamicPropertyAccessor dpa;
            object value = DependencyProperty.UnsetValue;
            SetPropertyInfo(_arySVS[level].info, out pi, out pd, out dp, out dpa); 

            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(d, dp))
                        value = d.GetValue(dp); 
                    else 
                        value = BindingExpression.IgnoreDefaultValue;
                } 
                else if (dpa != null)
                {
                    value = dpa.GetValue(item);
                } 
                break;
 
            case SourceValueType.Indexer: 
                DynamicIndexerAccessor dia;
                // 
                if (pi != null)
                {
                    object[] args = _arySVS[level].args;
 
                    IListIndexerArg wrapper;
                    if (args != null && args.Length == 1 && 
                        (wrapper = args[0] as IListIndexerArg) != null) 
                    {
                        // common special case: IList indexer.  Avoid 
                        // out-of-range exceptions.
                        int index = wrapper.Value;
                        IList ilist = (IList)item;
 
                        if (0 <= index && index < ilist.Count)
                        { 
                            value = ilist[index]; 
                        }
                        else 
                        {
                            value = IListIndexOutOfRange;
                        }
                    } 
                    else
                    { 
                        // normal case 
                        value = pi.GetValue(item,
                                        BindingFlags.GetProperty, null, 
                                        args,
                                        CultureInfo.InvariantCulture);
                    }
                } 
                else if ((dia = _arySVS[level].info as DynamicIndexerAccessor) != null)
                { 
                    value = dia.GetValue(item, _arySVS[level].args); 
                }
                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; 
            DynamicPropertyAccessor dpa;
            int level = _arySVS.Length - 1; 
            SetPropertyInfo(_arySVS[level].info, out pi, out pd, out dp, out dpa);

            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); 
                }
                else if (dpa != null)
                {
                    dpa.SetValue(item, value); 
                }
                break; 
 
            case SourceValueType.Indexer:
                DynamicIndexerAccessor dia; 
                //
                if (pi != null)
                {
                    pi.SetValue(item, value, 
                                    BindingFlags.SetProperty, null,
                                    GetIndexerArguments(level), 
                                    CultureInfo.InvariantCulture); 
                }
                else if ((dia = _arySVS[level].info as DynamicIndexerAccessor) != null) 
                {
                    dia.SetValue(item, _arySVS[level].args, value);
                }
                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; 
                    DynamicPropertyAccessor dpa; 
                    SetPropertyInfo(GetAccessor(level), out pi, out pd, out dp, out dpa);
 
                    string name =   (dp != null) ? dp.Name :
                                    (pi != null) ? pi.Name :
                                    (pd != null) ? pd.Name :
                                    (dpa != null ) ? dpa.PropertyName : 
                                    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 IsCritical[Application]Exception() handles these correctly.
                        #pragma warning disable 56500

                        try 
                        {
                            error = idei[name]; 
                        } 
                        catch (Exception ex)
                        { 
                            if (CriticalExceptions.IsCriticalApplicationException(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)
            {
                if (initialLevel < _arySVS.Length)
                { 
                    // when something in the path changes, recompute whether we
                    // need direct notifications from the raw value 
                    NeedsDirectNotification = _status == PropertyPathStatus.Active && 
                            _arySVS.Length > 0 &&
                            SVI[_arySVS.Length-1].type != SourceValueType.Direct && 
                            !(_arySVS[_arySVS.Length-1].info is DependencyProperty) &&
                            typeof(DependencyObject).IsAssignableFrom(_arySVS[_arySVS.Length-1].type);
                }
 
                _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 ||
                parent == BindingExpressionBase.DisconnectedItem) 
            {
                _arySVS[k].item = BindingExpression.ReplaceReference(_arySVS[k].item, newO); 
 
                if (parent == DependencyProperty.UnsetValue ||
                    parent == BindingExpression.NullDataItem || 
                    parent == BindingExpressionBase.DisconnectedItem)
                {
                    _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)
            {
                parent = newO;              // used by error reporting
                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;

                        // if there's no current item because parent is an empty 
                        // XmlDataCollection, treat it as a path error (the XPath
                        // didn't return any nodes) 
                        if (!IsEmptyXmlDataCollection(parent)) 
                        {
                            // otherwise it's not an error - currency is simply 
                            // off the collection
                            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) 
                {
                    if (!IsEmptyXmlDataCollection(parent)) 
                    {
                        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 || 
                                parent == DataGrid.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); 
                        } 
                    }
                    else 
                    {
                        TraceEventType traceType = (bindingExpression != null) ? bindingExpression.TraceLevel : TraceEventType.Error;
                        _host.ReportBadXPath(traceType);
                    } 

                } 
            } 
        }
 
        // determine if the cached state of the path is still correct.  This is
        // used to deduce whether event leapfrogging has occurred along the path
        // (i.e. something changed, but we haven't yet received the notification)
        internal bool IsPathCurrent(object rootItem) 
        {
            if (Status != PropertyPathStatus.Active) 
                return false; 

            object item = rootItem; 
            for (int level=0, n=Length; level 0 : false;
        } 

        // 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;
                DynamicObjectAccessor doa; 
                PropertyPath.DowncastAccessor(info, out dp, out pi1, out pd, out doa);
 
                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; 
                }
                else if (pd != null) 
                { 
                    sourceType = pd.PropertyType;
                } 
                else if (doa != null)
                {
                    sourceType = doa.PropertyType;
#if DEBUG 
                    checkCacheResult = false;      // not relevant for dynamic objects
#endif 
                } 
                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, ref 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 };
                bool isIList = (item is IList);
                if (isIList) 
                    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(ParameterInfo[] aryPI, IndexerParameterInfo[] aryInfo, object[] args, bool isIList) 
        { 
            // must have the right number of parameters
            if (aryPI != null && 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 IsCritical[Application]Exception() 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.IsCriticalApplicationException(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")));
                } 
 
                // catch the pseudo-exception as well
                if (o == IListIndexOutOfRange) 
                {
                    o = DependencyProperty.UnsetValue;
                    if (_host != null)
                        _host.ReportGetValueError(k, item, new ArgumentOutOfRangeException("index")); 
                }
 
                #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, out DynamicPropertyAccessor dpa)
        { 
            pi = null;
            pd = null; 
            dpa = null; 
            dp = info as DependencyProperty;
 
            if (dp == null)
            {
                pi = info as PropertyInfo;
                if (pi == null) 
                {
                    pd = info as PropertyDescriptor; 
 
                    if (pd == null)
                        dpa = info as DynamicPropertyAccessor; 
                }
            }
        }
 
        void CheckReadOnly(object item, object info)
        { 
            PropertyInfo pi; 
            PropertyDescriptor pd;
            DependencyProperty dp; 
            DynamicPropertyAccessor dpa;
            SetPropertyInfo(info, out pi, out pd, out dp, out dpa);

            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));
            } 
            else if (dpa != null)
            {
                if (dpa.IsReadOnly)
                    throw new InvalidOperationException(SR.Get(SRID.CannotWriteToReadOnly, item.GetType(), dpa.PropertyName)); 
            }
        } 
 
        // see whether DBNull is a valid value for update, and cache the answer
        void DetermineWhetherDBNullIsValid() 
        {
            bool result = false;
            object item = GetItem(Length - 1);
 
            if (item != null && AssemblyHelper.IsLoaded(UncommonAssembly.System_Data))
            { 
                result = DetermineWhetherDBNullIsValid(item); 
            }
 
            _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) 
        { 
            System.Data.DataRowView drv;
            System.Data.DataRow dr = null; 
            if ((drv = item as System.Data.DataRowView) == null &&
                (dr = item as System.Data.DataRow) == null)
            {
                return false; 
            }
 
            PropertyInfo pi; 
            PropertyDescriptor pd;
            DependencyProperty dp; 
            DynamicPropertyAccessor dpa;
            SetPropertyInfo(_arySVS[Length-1].info, out pi, out pd, out dp, out dpa);

            // this code was provided by the ADO team 
            System.Data.DataTable table = (drv != null) ? drv.DataView.Table : dr.Table;
            string columnName = (pd != null) ? pd.Name : 
                                (pi != null) ? pi.Name : null; 

            System.Data.DataColumn column = null; 
            if (columnName == "Item" && pi != null)
            {
                object arg = _arySVS[Length-1].args[0];
                if ((columnName = arg as String) != null) 
                {
                    column = table.Columns[columnName]; 
                } 
                else if (arg is int)
                { 
                    int index = (int)arg;
                    if (0 <= index && index < table.Columns.Count)
                    {
                        column = table.Columns[index]; 
                    }
                } 
            } 
            else if (columnName != null)
            { 
                column = table.Columns[columnName];
            }

            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; 
                GC.SuppressFinalize(this);
            } 
        } 

        // wrapper for arguments to IList indexer 
        class IListIndexerArg
        {
            public IListIndexerArg(int arg)
            { 
                _arg = arg;
            } 
 
            public int Value { get { return _arg; } }
 
            int _arg;
        }

        //----------------------------------------------------- 
        //
        //  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 NamedObject("NoParent"); 
        static readonly object AsyncRequestPending = new NamedObject("AsyncRequestPending");
        internal static readonly object IListIndexOutOfRange = new NamedObject("IListIndexOutOfRange"); 
 
        //------------------------------------------------------
        // 
        //  Private Fields
        //
        //-----------------------------------------------------
 
        PropertyPath        _parent;
        PropertyPathStatus  _status; 
        object              _treeContext; 
        object              _rootItem;
        SourceValueState[]  _arySVS; 
        ContextHelper       _contextHelper;

        ClrBindingWorker    _host;
        DataBindEngine      _engine; 

        bool                _dependencySourcesChanged; 
        bool                _isDynamic; 
        bool                _needsDirectNotification;
        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