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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- ResourceAssociationTypeEnd.cs
- ImageList.cs
- LambdaCompiler.Generated.cs
- WebEventTraceProvider.cs
- ManipulationDeltaEventArgs.cs
- HostedAspNetEnvironment.cs
- AssociatedControlConverter.cs
- ClassHandlersStore.cs
- HtmlImage.cs
- WebZone.cs
- MetadataCache.cs
- DataServices.cs
- ElapsedEventArgs.cs
- InputMethod.cs
- ConversionContext.cs
- ContextBase.cs
- StylusPointCollection.cs
- ToolStripItemRenderEventArgs.cs
- SnapshotChangeTrackingStrategy.cs
- SvcMapFileLoader.cs
- LocalizationCodeDomSerializer.cs
- BulletedList.cs
- ObsoleteAttribute.cs
- AspCompat.cs
- ListViewSortEventArgs.cs
- ExceptionUtil.cs
- securitymgrsite.cs
- ColumnBinding.cs
- prompt.cs
- XmlParserContext.cs
- DataGridViewUtilities.cs
- WebReference.cs
- CompositeCollection.cs
- CompressStream.cs
- OdbcPermission.cs
- DebugInfoGenerator.cs
- ConditionalExpression.cs
- TreeSet.cs
- ClientCultureInfo.cs
- GeometryDrawing.cs
- ConstraintStruct.cs
- NonClientArea.cs
- TrailingSpaceComparer.cs
- PolyQuadraticBezierSegment.cs
- EdmComplexTypeAttribute.cs
- DeploymentSection.cs
- AliasedExpr.cs
- DataGridTextBox.cs
- ValidationHelper.cs
- AbandonedMutexException.cs
- PersonalizationProviderCollection.cs
- SiteMapDataSource.cs
- HandoffBehavior.cs
- ColumnHeaderConverter.cs
- BitmapEffectState.cs
- EpmCustomContentSerializer.cs
- Path.cs
- ObjectDataSourceSelectingEventArgs.cs
- SignatureDescription.cs
- SystemIPGlobalProperties.cs
- ShaderRenderModeValidation.cs
- CommandConverter.cs
- ColorTranslator.cs
- HtmlElementEventArgs.cs
- CapabilitiesUse.cs
- ValueProviderWrapper.cs
- ColorBlend.cs
- TransformGroup.cs
- ASCIIEncoding.cs
- _SSPISessionCache.cs
- DocumentXmlWriter.cs
- Vector.cs
- ByteStorage.cs
- TreeViewHitTestInfo.cs
- NativeMethods.cs
- CodeAccessPermission.cs
- SchemaImporter.cs
- StylusTip.cs
- CodeDomConfigurationHandler.cs
- CodeArrayIndexerExpression.cs
- DocumentReference.cs
- DSASignatureDeformatter.cs
- SQLMoneyStorage.cs
- XmlTextReader.cs
- Empty.cs
- RelatedImageListAttribute.cs
- IsolatedStoragePermission.cs
- UserPreferenceChangingEventArgs.cs
- ProfilePropertyNameValidator.cs
- FileDialog_Vista.cs
- ObjectReaderCompiler.cs
- LazyTextWriterCreator.cs
- ConfigUtil.cs
- TemplateBindingExtension.cs
- XmlAnyElementAttribute.cs
- SyndicationSerializer.cs
- ImplicitInputBrush.cs
- TreeWalkHelper.cs
- BindValidator.cs
- EncryptedPackage.cs