/// <summary> /// Invalidate inheritable properties and resource /// references during a tree change operation. /// </summary> internal static void InvalidateOnTreeChange( FrameworkElement fe, FrameworkContentElement fce, DependencyObject parent, bool isAddOperation) { Debug.Assert(fe != null || fce != null, "Node with the tree change notification must be an FE or an FCE."); Debug.Assert(parent != null, "Must have a parent that the current node is connected to or disconnected from."); // If the tree change is for a non-FE/FCE parent then we need to find // the nearest FE/FCE parent inorder to propagate inheritance correctly. FrameworkObject parentFO = new FrameworkObject(parent); if (!parentFO.IsValid) { parent = parentFO.FrameworkParent.DO; } // We're interested in changes to the Template property that occur during // the walk - if the template has changed we don't need to invalidate // template-driven properties a second time. The HasTemplateChanged property // is cleared on the first visit to each node, so that it means "template // changed during the walk". But one relevant node isn't visited during // the walk - the templated parent of the initial node. So we handle that now. FrameworkObject fo = new FrameworkObject(fe, fce); // Synchronize the ShouldLookupImplicitStyles flag with respect to the parent here // because for the root node of a tree change UpdateStyleProperty happens right here // in this method. And we need to have synchrnozed the ShouldLookupImplicitStyles // before we re-query the Style property. if (isAddOperation) { fo.SetShouldLookupImplicitStyles(); } fo.Reset(fo.TemplatedParent); fo.HasTemplateChanged = false; DependencyObject d = (fe != null) ? (DependencyObject)fe : (DependencyObject)fce; // during a tree walk to invalidate inherited properties, we typically // call UpdateStyle from FE/FCE.InvalidateTreeDependentProperties. But // for the root element of the tree change, we need to record old values // for inherited properties before we've updated the inheritance parent; // so do the updatestyle here before we record old values so that we // capture any updates provided by styles. if (fe != null) { if (fe.IsInitialized && !fe.HasLocalStyle) { // Clear the HasStyleChanged flag fe.HasStyleChanged = false; fe.HasStyleInvalidated = false; fe.HasTemplateChanged = false; fe.AncestorChangeInProgress = true; fe.UpdateStyleProperty(); fe.AncestorChangeInProgress = false; } } else { if (!fce.HasLocalStyle) { // Clear the HasStyleChanged flag fce.HasStyleChanged = false; fce.HasStyleInvalidated = false; fce.AncestorChangeInProgress = true; fce.UpdateStyleProperty(); fce.AncestorChangeInProgress = false; } } if (HasChildren(fe, fce)) { // Spin up a DescendentsWalker only when // the current node has children to walk // If there is another tree walk that has already visited the // current node then we do not need to re-walk its sub-tree. FrameworkContextData fcdata = FrameworkContextData.From(d.Dispatcher); if (!fcdata.WasNodeVisited(d, TreeChangeDelegate)) { // The TreeChangeInfo object is used here to track // information that we have because we're doing a tree walk. TreeChangeInfo parentInfo = new TreeChangeInfo(d, parent, isAddOperation); // PrePostDescendentsWalker is used instead of the standard // DescendentsWalker because we need a "post" callback to know when // to pop the parent's InheritableProperties cache from the stack. PrePostDescendentsWalker<TreeChangeInfo> walker = new PrePostDescendentsWalker<TreeChangeInfo>( TreeWalkPriority.LogicalTree, TreeChangeDelegate, TreeChangePostDelegate, parentInfo); fcdata.AddWalker(TreeChangeDelegate, walker); try { walker.StartWalk(d); } finally { fcdata.RemoveWalker(TreeChangeDelegate, walker); } } } else { // Degenerate case when the current node is a leaf node and has no children. TreeChangeInfo parentInfo = new TreeChangeInfo(d, parent, isAddOperation); // Degenerate case of OnAncestorChanged for a single node OnAncestorChanged(fe, fce, parentInfo); // Degenerate case of OnPostAncestorChanged for a single node OnPostAncestorChanged(d, parentInfo); } }
/// <summary> /// Process a resource change for the given DependencyObject. /// Return true if the DO has resource references. /// </summary> internal static void OnResourcesChanged( DependencyObject d, ResourcesChangeInfo info, bool raiseResourceChangedEvent) { Debug.Assert(d != null, "Must have non-null current node"); bool containsTypeOfKey = info.Contains(d.DependencyObjectType.SystemType, true /*isImplicitStyleKey*/); bool isSystemResourcesChange = info.IsThemeChange; bool isStyleResourcesChange = info.IsStyleResourcesChange; bool isTemplateResourcesChange = info.IsTemplateResourcesChange; bool isContainer = (info.Container == d); FrameworkObject fo = new FrameworkObject(d); // If a resource dictionary changed above this node then we need to // synchronize the ShouldLookupImplicitStyles flag with respect to // our parent here. if (info.IsResourceAddOperation || info.IsCatastrophicDictionaryChange) { fo.SetShouldLookupImplicitStyles(); } // Invalidate implicit and explicit resource // references on current instance if (fo.IsFE) { // If this is a FrameworkElement FrameworkElement fe = fo.FE; fe.HasStyleChanged = false; // detect style changes that arise from work done here fe.HasStyleInvalidated = false; fe.HasTemplateChanged = false; // detect template changes that arise from work done here if (fe.HasResourceReference) { // Invalidate explicit ResourceReference properties on the current instance. // If the Style property comes from an implicit resource reference that // will be invalidated too. InvalidateResourceReferences(fe, info); // There is no need to invalidate the resources references on the // container object if this call is a result of a style/template // change. This is because the style/template change would have // already invalidated all the container dependents and all the // resources references on the container would have been a part of it. if ((!isStyleResourcesChange && !isTemplateResourcesChange ) || !isContainer) { InvalidateStyleAndReferences(d, info, containsTypeOfKey); } } else if (containsTypeOfKey && (fe.HasImplicitStyleFromResources || fe.Style == FrameworkElement.StyleProperty.GetMetadata(fe.DependencyObjectType).DefaultValue)) { // If The Style property on the given instance has been // fetched by an implicit resource lookup then // it needs to be invalidated. Also we need to do this // invalidation only if the dictionary/resources that is // changing matches the implicit key used for the resource lookup. // The StyleProperty does not need to be invalidated if this // call is the result of a style change if (!isStyleResourcesChange || !isContainer) { fe.UpdateStyleProperty(); } } // If there has been a Theme change then // invalidate the ThemeStyleProperty if (isSystemResourcesChange) { fe.UpdateThemeStyleProperty(); } // Raise the ResourcesChanged Event so that ResourceReferenceExpressions // on non-[FE/FCE] (example Freezables) listening for this can then update // their values if (raiseResourceChangedEvent && fe.PotentiallyHasMentees) { fe.RaiseClrEvent(FrameworkElement.ResourcesChangedKey, new ResourcesChangedEventArgs(info)); } } else { // If this is a FrameworkContentElement FrameworkContentElement fce = fo.FCE; fce.HasStyleChanged = false; // detect style changes that arise from work done here fce.HasStyleInvalidated = false; if (fce.HasResourceReference) { // Invalidate explicit ResourceReference properties on the current instance. // If the Style property comes from an implicit resource reference that // will be invalidated too. InvalidateResourceReferences(fce, info); // There is no need to invalidate the resources references on the // container object if this call is a result of a style/template // change. This is because the style/template change would have // already invalidated all the container dependents and all the // resources references on the container would have been a part of it. if ((!isStyleResourcesChange && !isTemplateResourcesChange ) || !isContainer) { InvalidateStyleAndReferences(d, info, containsTypeOfKey); } } else if (containsTypeOfKey && (fce.HasImplicitStyleFromResources || fce.Style == FrameworkContentElement.StyleProperty.GetMetadata(fce.DependencyObjectType).DefaultValue)) { // If The Style property on the given instance has been // fetched by an implicit resource lookup then // it needs to be invalidated. Also we need to do this // invalidation only if the dictionary/resources that is // changing matches the implicit key used for the resource lookup. // The StyleProperty does not need to be invalidated if this // call is the result of a style change if (!isStyleResourcesChange || !isContainer) { fce.UpdateStyleProperty(); } } // If there has been a Theme change then // invalidate the ThemeStyleProperty if (isSystemResourcesChange) { fce.UpdateThemeStyleProperty(); } // Raise the ResourcesChanged Event so that ResourceReferenceExpressions // on non-[FE/FCE] (example Freezables) listening for this can then update // their values if (raiseResourceChangedEvent && fce.PotentiallyHasMentees) { fce.RaiseClrEvent(FrameworkElement.ResourcesChangedKey, new ResourcesChangedEventArgs(info)); } } }
internal void OnAncestorChangedInternal(TreeChangeInfo parentTreeState) { // Cache the IsSelfInheritanceParent flag bool isSelfInheritanceParent = IsSelfInheritanceParent; if (parentTreeState.Root != this) { // Clear the HasStyleChanged flag HasStyleChanged = false; HasStyleInvalidated = false; } // If this is a tree add operation update the ShouldLookupImplicitStyles // flag with respect to your parent. if (parentTreeState.IsAddOperation) { FrameworkObject fo = new FrameworkObject(null, this); fo.SetShouldLookupImplicitStyles(); } // Invalidate ResourceReference properties if (HasResourceReference) { // This operation may cause a style change and hence should be done before the call to // InvalidateTreeDependents as it relies on the HasStyleChanged flag TreeWalkHelper.OnResourcesChanged(this, ResourcesChangeInfo.TreeChangeInfo, false); } // If parent is a FrameworkElement // This is also an operation that could change the style FrugalObjectList<DependencyProperty> currentInheritableProperties = InvalidateTreeDependentProperties(parentTreeState, isSelfInheritanceParent); // we have inherited properties that changes as a result of the above; // invalidation; push that list of inherited properties on the stack // for the children to use parentTreeState.InheritablePropertiesStack.Push(currentInheritableProperties); // Notify the PresentationSource that this element's ancestry may have changed. // We only need the ContentElement's because UIElements are taken care of // through the Visual class. PresentationSource.OnAncestorChanged(this); // Call OnAncestorChanged OnAncestorChanged(); // Notify mentees if they exist if (PotentiallyHasMentees) { // Raise the ResourcesChanged Event so that ResourceReferenceExpressions // on non-[FE/FCE] listening for this can then update their values RaiseClrEvent(FrameworkElement.ResourcesChangedKey, EventArgs.Empty); } }