| File: System\Windows\TreeChangeInfo.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // // // Description: // This data-structure is used // 1. As the data that is passed around by the DescendentsWalker // during an AncestorChanged tree-walk. // // using MS.Internal; using MS.Utility; namespace System.Windows { /// <summary> /// This is the data that is passed through the DescendentsWalker /// during an AncestorChange tree-walk. /// </summary> internal struct TreeChangeInfo { #region Properties public TreeChangeInfo(DependencyObject root, DependencyObject parent, bool isAddOperation) { _rootOfChange = root; _isAddOperation = isAddOperation; _topmostCollapsedParentNode = null; _rootInheritableValues = null; _inheritablePropertiesStack = null; _valueIndexer = 0; // Create the InheritableProperties cache for the parent // and push it to start the stack ... we don't need to // pop this when we're done because we'll be throing the // stack away at that point. InheritablePropertiesStack.Push(CreateParentInheritableProperties(root, parent, isAddOperation)); } // // This method // 1. Is called from AncestorChange InvalidateTree. // 2. It is used to create the InheritableProperties on the given node. // 3. It also accumulates oldValues for the inheritable properties that are about to be invalidated // internal FrugalObjectList<DependencyProperty> CreateParentInheritableProperties( DependencyObject d, DependencyObject parent, bool isAddOperation) { Debug.Assert(d != null, "Must have non-null current node"); if (parent == null) { return new FrugalObjectList<DependencyProperty>(0); } DependencyObjectType treeObjDOT = d.DependencyObjectType; // See if we have a cached value. EffectiveValueEntry[] parentEffectiveValues = null; uint parentEffectiveValuesCount = 0; uint inheritablePropertiesCount = 0; // If inheritable properties aren't cached on you then use the effective // values cache on the parent to discover those inherited properties that // may need to be invalidated on the children nodes. if (!parent.IsSelfInheritanceParent) { DependencyObject inheritanceParent = parent.InheritanceParent; if (inheritanceParent != null) { parentEffectiveValues = inheritanceParent.EffectiveValues; parentEffectiveValuesCount = inheritanceParent.EffectiveValuesCount; inheritablePropertiesCount = inheritanceParent.InheritableEffectiveValuesCount; } } else { parentEffectiveValues = parent.EffectiveValues; parentEffectiveValuesCount = parent.EffectiveValuesCount; inheritablePropertiesCount = parent.InheritableEffectiveValuesCount; } FrugalObjectList<DependencyProperty> inheritableProperties = new FrugalObjectList<DependencyProperty>((int) inheritablePropertiesCount); if (inheritablePropertiesCount == 0) { return inheritableProperties; } _rootInheritableValues = new InheritablePropertyChangeInfo[(int) inheritablePropertiesCount]; int inheritableIndex = 0; FrameworkObject foParent = new FrameworkObject(parent); for (uint i=0; i<parentEffectiveValuesCount; i++) { // Add all the inheritable properties from the effectiveValues // cache to the TreeStateCache on the parent EffectiveValueEntry entry = parentEffectiveValues[i]; DependencyProperty dp = DependencyProperty.RegisteredPropertyList.List[entry.PropertyIndex]; // There are UncommonFields also stored in the EffectiveValues cache. We need to exclude those. if ((dp != null) && dp.IsPotentiallyInherited) { PropertyMetadata metadata = dp.GetMetadata(parent.DependencyObjectType); if (metadata != null && metadata.IsInherited) { Debug.Assert(!inheritableProperties.Contains(dp), "EffectiveValues cache must not contains duplicate entries for the same DP"); FrameworkPropertyMetadata fMetadata = (FrameworkPropertyMetadata)metadata; // Children do not need to inherit properties across a tree boundary // unless the property is set to override this behavior. if (!TreeWalkHelper.SkipNow(foParent.InheritanceBehavior) || fMetadata.OverridesInheritanceBehavior) { inheritableProperties.Add(dp); EffectiveValueEntry oldEntry; EffectiveValueEntry newEntry; oldEntry = d.GetValueEntry( d.LookupEntry(dp.GlobalIndex), dp, dp.GetMetadata(treeObjDOT), RequestFlags.DeferredReferences); if (isAddOperation) { // set up the new value newEntry = entry; if ((newEntry.BaseValueSourceInternal != BaseValueSourceInternal.Default) || newEntry.HasModifiers) { newEntry = newEntry.GetFlattenedEntry(RequestFlags.FullyResolved); newEntry.BaseValueSourceInternal = BaseValueSourceInternal.Inherited; } } else { newEntry = new EffectiveValueEntry(); } _rootInheritableValues[inheritableIndex++] = new InheritablePropertyChangeInfo(d, dp, oldEntry, newEntry); if (inheritablePropertiesCount == inheritableIndex) { // no more inheritable properties, bail early break; } } } } } return inheritableProperties; } // This is called by TreeWalker.InvalidateTreeDependentProperties before looping through // all of the items in the InheritablesPropertiesStack internal void ResetInheritableValueIndexer() { _valueIndexer = 0; } // This is called by TreeWalker.InvalidateTreeDependentProperty. // _valueIndexer is an optimization because we know (a) that the last DP list pushed on the // InheritablePropertiesStack is a subset of the first DP list pushed on this stack; // (b) the first DP list pushed on the stack has the same # (and order) of entries as the // RootInheritableValues list; and (c) the last DP list pushed on the stack will have its // DPs in the same order as those DPs appear in the first DP list pushed on the stack. This // allows us to simply increment _valueIndexer until we find a match; and on subsequent // calls to GetRootInheritableValue just continue our incrementing from where we left off // the last time. internal InheritablePropertyChangeInfo GetRootInheritableValue(DependencyProperty dp) { InheritablePropertyChangeInfo info; do { info = _rootInheritableValues[_valueIndexer++]; } while (info.Property != dp); return info; } /// <summary> /// This is a stack that is used during an AncestorChange operation. /// I holds the InheritableProperties cache on the parent nodes in /// the tree in the order of traversal. The top of the stack holds /// the cache for the immediate parent node. /// </summary> internal Stack<FrugalObjectList<DependencyProperty>> InheritablePropertiesStack { get { if (_inheritablePropertiesStack == null) { _inheritablePropertiesStack = new Stack<FrugalObjectList<DependencyProperty>>(1); } return _inheritablePropertiesStack; } } /// <summary> /// When we enter a Visibility=Collapsed subtree, we remember that node /// using this property. As we process the children of this collapsed /// node, we see this property as non-null and know we're collapsed. /// As we exit the subtree, this reference is nulled out which means /// we don't know whether we're in a collapsed tree and the optimizations /// based on Visibility=Collapsed are not valid and should not be used. /// </summary> internal object TopmostCollapsedParentNode { get { return _topmostCollapsedParentNode; } set { _topmostCollapsedParentNode = value; } } // Indicates if this is a add child tree operation internal bool IsAddOperation { get { return _isAddOperation; } } // This is the element at the root of the sub-tree that had a parent change. internal DependencyObject Root { get { return _rootOfChange; } } #endregion Properties #region Data private Stack<FrugalObjectList<DependencyProperty>> _inheritablePropertiesStack; private object _topmostCollapsedParentNode; private bool _isAddOperation; private DependencyObject _rootOfChange; private InheritablePropertyChangeInfo[] _rootInheritableValues; private int _valueIndexer; #endregion Data } }