internal void SetEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry newEntry, EffectiveValueEntry oldEntry) { if (metadata != null && metadata.IsInherited && (newEntry.BaseValueSourceInternal != BaseValueSourceInternal.Inherited || newEntry.IsCoerced || newEntry.IsAnimated) && !IsSelfInheritanceParent) { SetIsSelfInheritanceParent(); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } bool restoreMarker = false; if (oldEntry.HasExpressionMarker && !newEntry.HasExpressionMarker) { BaseValueSourceInternal valueSource = newEntry.BaseValueSourceInternal; restoreMarker = (valueSource == BaseValueSourceInternal.ThemeStyle || valueSource == BaseValueSourceInternal.ThemeStyleTrigger || valueSource == BaseValueSourceInternal.Style || valueSource == BaseValueSourceInternal.TemplateTrigger || valueSource == BaseValueSourceInternal.StyleTrigger || valueSource == BaseValueSourceInternal.ParentTemplate || valueSource == BaseValueSourceInternal.ParentTemplateTrigger); } if (restoreMarker) { newEntry.RestoreExpressionMarker(); } else if (oldEntry.IsExpression && oldEntry.ModifiedValue.ExpressionValue == Expression.NoValue) { // we now have a value for an expression that is "hiding" - save it // as the expression value newEntry.SetExpressionValue(newEntry.Value, oldEntry.ModifiedValue.BaseValue); } #if DEBUG object baseValue; if (!newEntry.HasModifiers) { baseValue = newEntry.Value; } else { if (newEntry.IsCoercedWithCurrentValue) { baseValue = newEntry.ModifiedValue.CoercedValue; } else if (newEntry.IsExpression) { baseValue = newEntry.ModifiedValue.ExpressionValue; } else { baseValue = newEntry.ModifiedValue.BaseValue; } } Debug.Assert(newEntry.IsDeferredReference == (baseValue is DeferredReference)); #endif if (entryIndex.Found) { _effectiveValues[entryIndex.Index] = newEntry; } else { InsertEntry(newEntry, entryIndex.Index); if (metadata != null && metadata.IsInherited) { InheritableEffectiveValuesCount++; } } Debug.Assert(dp == null || (dp.GlobalIndex == newEntry.PropertyIndex), "EffectiveValueEntry & DependencyProperty do not match"); }
[FriendAccessAllowed] // Declared in Base also used in Framework internal UpdateResult UpdateEffectiveValue( EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, ref EffectiveValueEntry newEntry, bool coerceWithDeferredReference, bool coerceWithCurrentValue, OperationType operationType) { if (dp == null) { throw new ArgumentNullException("dp"); } #region EventTracing #if VERBOSE_PROPERTY_EVENT bool isDynamicTracing = EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.verbose); // This was under "normal" if (isDynamicTracing) { ++InvalidationCount; if( InvalidationCount % 100 == 0 ) { EventTrace.EventProvider.TraceEvent(EventTrace.PROPERTYINVALIDATIONGUID, MS.Utility.EventType.Info, InvalidationCount ); } string TypeAndName = String.Format(CultureInfo.InvariantCulture, "[{0}]{1}({2})",GetType().Name,dp.Name,base.GetHashCode()); // FxCop wanted the CultureInfo.InvariantCulture EventTrace.EventProvider.TraceEvent(EventTrace.PROPERTYINVALIDATIONGUID, MS.Utility.EventType.StartEvent, base.GetHashCode(), TypeAndName); // base.GetHashCode() to avoid calling a virtual, which FxCop doesn't like. } #endif #endregion EventTracing #if NESTED_OPERATIONS_CHECK // Are we invalidating out of control? if( NestedOperations > NestedOperationMaximum ) { // We're invalidating out of control, time to abort. throw new InvalidOperationException("Too many levels of nested DependencyProperty invalidations. This usually indicates a circular reference in the application and the cycle needs to be broken."); } NestedOperations++; // Decrement in the finally block #endif int targetIndex = dp.GlobalIndex; if (oldEntry.BaseValueSourceInternal == BaseValueSourceInternal.Unknown) { // Do a full get value of the old entry if it isn't supplied. // It isn't supplied in cases where we are *unsetting* a value // (e.g. ClearValue, style unapply, trigger unapply) oldEntry = GetValueEntry( entryIndex, dp, metadata, RequestFlags.RawEntry); } object oldValue = oldEntry.GetFlattenedEntry(RequestFlags.FullyResolved).Value; /* if( TraceDependencyProperty.IsEnabled ) { TraceDependencyProperty.Trace( TraceEventType.Verbose, TraceDependencyProperty.UpdateEffectiveValueStart, this, dp, dp.OwnerType, oldValue, oldEntry.BaseValueSourceInternal ); } */ // for control-value coercion, extract the desired control value, then // reset the new entry to ask for a re-evaluation with coercion object controlValue = null; if (coerceWithCurrentValue) { controlValue = newEntry.Value; newEntry = new EffectiveValueEntry(dp, FullValueSource.IsCoerced); } // check for early-out opportunities: // 1) the new entry is of lower priority than the current entry if ((newEntry.BaseValueSourceInternal != BaseValueSourceInternal.Unknown) && (newEntry.BaseValueSourceInternal < oldEntry.BaseValueSourceInternal)) { return 0; } bool isReEvaluate = false; bool isCoerceValue = false; bool isClearValue = false; if (newEntry.Value == DependencyProperty.UnsetValue) { FullValueSource fullValueSource = newEntry.FullValueSource; isCoerceValue = (fullValueSource == FullValueSource.IsCoerced); isReEvaluate = true; if (newEntry.BaseValueSourceInternal == BaseValueSourceInternal.Local) { isClearValue = true; } } // if we're not in an animation update (caused by AnimationStorage.OnCurrentTimeInvalidated) // then always force a re-evaluation if (a) there was an animation in play or (b) there's // an expression evaluation to be made if (isReEvaluate || (!newEntry.IsAnimated && (oldEntry.IsAnimated || (oldEntry.IsExpression && newEntry.IsExpression && (newEntry.ModifiedValue.BaseValue == oldEntry.ModifiedValue.BaseValue))))) { // we have to compute the new value if (!isCoerceValue) { newEntry = EvaluateEffectiveValue(entryIndex, dp, metadata, oldEntry, newEntry, operationType); // Make sure that the call out did not cause a change to entryIndex entryIndex = CheckEntryIndex(entryIndex, targetIndex); bool found = (newEntry.Value != DependencyProperty.UnsetValue); if (!found && metadata.IsInherited) { DependencyObject inheritanceParent = InheritanceParent; if (inheritanceParent != null) { // Fetch the IsDeferredValue flag from the InheritanceParent EntryIndex parentEntryIndex = inheritanceParent.LookupEntry(dp.GlobalIndex); if (parentEntryIndex.Found) { found = true; newEntry = inheritanceParent._effectiveValues[parentEntryIndex.Index].GetFlattenedEntry(RequestFlags.FullyResolved); newEntry.BaseValueSourceInternal = BaseValueSourceInternal.Inherited; } } } // interesting that I just had to add this ... suggests that we are now overinvalidating if (!found) { newEntry = EffectiveValueEntry.CreateDefaultValueEntry(dp, metadata.GetDefaultValue(this, dp)); } } else { if (!oldEntry.HasModifiers) { newEntry = oldEntry; } else { newEntry = new EffectiveValueEntry(dp, oldEntry.BaseValueSourceInternal); ModifiedValue modifiedValue = oldEntry.ModifiedValue; object baseValue = modifiedValue.BaseValue; newEntry.Value = baseValue; newEntry.HasExpressionMarker = oldEntry.HasExpressionMarker; if (oldEntry.IsExpression) { newEntry.SetExpressionValue(modifiedValue.ExpressionValue, baseValue); } if (oldEntry.IsAnimated) { newEntry.SetAnimatedValue(modifiedValue.AnimatedValue, baseValue); } } } } // Coerce to current value if (coerceWithCurrentValue) { object baseValue = newEntry.GetFlattenedEntry(RequestFlags.CoercionBaseValue).Value; ProcessCoerceValue( dp, metadata, ref entryIndex, ref targetIndex, ref newEntry, ref oldEntry, ref oldValue, baseValue, controlValue, null /*coerceValueCallback */, coerceWithDeferredReference, coerceWithCurrentValue, false /*skipBaseValueChecks*/); // Make sure that the call out did not cause a change to entryIndex entryIndex = CheckEntryIndex(entryIndex, targetIndex); } // Coerce Value if (metadata.CoerceValueCallback != null && !(isClearValue && newEntry.FullValueSource == (FullValueSource)BaseValueSourceInternal.Default)) { // CALLBACK object baseValue = newEntry.GetFlattenedEntry(RequestFlags.CoercionBaseValue).Value; ProcessCoerceValue( dp, metadata, ref entryIndex, ref targetIndex, ref newEntry, ref oldEntry, ref oldValue, baseValue, null /* controlValue */, metadata.CoerceValueCallback, coerceWithDeferredReference, false /* coerceWithCurrentValue */, false /*skipBaseValueChecks*/); // Make sure that the call out did not cause a change to entryIndex entryIndex = CheckEntryIndex(entryIndex, targetIndex); } // The main difference between this callback and the metadata.CoerceValueCallback is that // designers want to be able to coerce during all value changes including a change to the // default value. Whereas metadata.CoerceValueCallback coerces all property values but the // default, because default values are meant to fit automatically fit into the coersion constraint. if (dp.DesignerCoerceValueCallback != null) { // During a DesignerCoerceValueCallback the value obtained is stored in the same // member as the metadata.CoerceValueCallback. In this case we do not store the // baseValue in the entry. Thus the baseValue checks will the violated. That is the // reason for skipping these checks in this one case. // Also before invoking the DesignerCoerceValueCallback the baseValue must // always be expanded if it is a DeferredReference ProcessCoerceValue( dp, metadata, ref entryIndex, ref targetIndex, ref newEntry, ref oldEntry, ref oldValue, newEntry.GetFlattenedEntry(RequestFlags.FullyResolved).Value, null /*controlValue*/, dp.DesignerCoerceValueCallback, false /*coerceWithDeferredReference*/, false /*coerceWithCurrentValue*/, true /*skipBaseValueChecks*/); // Make sure that the call out did not cause a change to entryIndex entryIndex = CheckEntryIndex(entryIndex, targetIndex); } UpdateResult result = 0; if (newEntry.FullValueSource != (FullValueSource) BaseValueSourceInternal.Default) { Debug.Assert(newEntry.BaseValueSourceInternal != BaseValueSourceInternal.Unknown, "Value source should be known at this point"); bool unsetValue = false; if (newEntry.BaseValueSourceInternal == BaseValueSourceInternal.Inherited) { if (DependencyObject.IsTreeWalkOperation(operationType) && (newEntry.IsCoerced || newEntry.IsAnimated)) { // an inherited value has been coerced or animated. This // should be treated as a new "set" of the property. // The current tree walk should not continue into the subtree, // but rather a new tree walk should start. // this signals OnPropertyChanged to start a new tree walk // and mark the current node as SelfInheritanceParent operationType = OperationType.Unknown; // this signals the caller not to continue the current // tree walk into the subtree result |= UpdateResult.InheritedValueOverridden; } else if (!IsSelfInheritanceParent) { // otherwise, just inherit the value from the InheritanceParent unsetValue = true; } } if (unsetValue) { UnsetEffectiveValue(entryIndex, dp, metadata); } else { SetEffectiveValue(entryIndex, dp, metadata, newEntry, oldEntry); } } else { UnsetEffectiveValue(entryIndex, dp, metadata); } // Change notifications are fired when the value actually changed or in // the case of the Freezable mutable factories when the value source changes. // Try AvaCop without the second condition to repro this problem. bool isAValueChange = !Equals(dp, oldValue, newEntry.GetFlattenedEntry(RequestFlags.FullyResolved).Value); if (isAValueChange) { result |= UpdateResult.ValueChanged; } if (isAValueChange || (operationType == OperationType.ChangeMutableDefaultValue && oldEntry.BaseValueSourceInternal != newEntry.BaseValueSourceInternal) || (metadata.IsInherited && oldEntry.BaseValueSourceInternal != newEntry.BaseValueSourceInternal && operationType != OperationType.AddChild && operationType != OperationType.RemoveChild && operationType != OperationType.Inherit)) { result |= UpdateResult.NotificationSent; try { // fire change notification NotifyPropertyChange( new DependencyPropertyChangedEventArgs( dp, metadata, isAValueChange, oldEntry, newEntry, operationType)); } finally { #if NESTED_OPERATIONS_CHECK NestedOperations--; #endif } } #region EventTracing #if VERBOSE_PROPERTY_EVENT if (isDynamicTracing) { if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.verbose)) { EventTrace.EventProvider.TraceEvent(EventTrace.PROPERTYINVALIDATIONGUID, MS.Utility.EventType.EndEvent); } } #endif #endregion EventTracing /* if( TraceDependencyProperty.IsEnabled ) { TraceDependencyProperty.Trace( TraceEventType.Verbose, TraceDependencyProperty.UpdateEffectiveValueStop, this, dp, dp.OwnerType, newEntry.Value, newEntry.BaseValueSourceInternal ); } */ // There are two cases in which we need to adjust inheritance contexts: // // 1. The value pointed to this DP has changed, in which case // we need to move the context from the old value to the // new value. // // 2. The value has not changed, but the ValueSource for the // property has. (For example, we've gone from being a local // value to the result of a binding expression that just // happens to return the same DO instance.) In which case // we may need to add or remove contexts even though we // did not raise change notifications. // // We don't want to provide an inheritance context if the entry is // animated, coerced, is an expression, is coming from a style or // template, etc. To avoid this, we explicitly check that the // FullValueSource is Local. By checking FullValueSource rather than // BaseValueSource we are implicitly filtering out any sources which // have modifiers. (e.g., IsExpression, IsAnimated, etc.) bool oldEntryHadContext = oldEntry.FullValueSource == (FullValueSource) BaseValueSourceInternal.Local; bool newEntryNeedsContext = newEntry.FullValueSource == (FullValueSource) BaseValueSourceInternal.Local; // NOTE: We use result rather than isAValueChange below so that we // pick up mutable default promotion, etc. if (result != 0 || (oldEntryHadContext != newEntryNeedsContext)) { if (oldEntryHadContext) { // RemoveSelfAsInheritanceContext no-ops null, non-DO values, etc. RemoveSelfAsInheritanceContext(oldEntry.LocalValue, dp); } // Become the context for the new value. This is happens after // invalidation so that FE has a chance to hookup the logical // tree first. This is done only if the current DependencyObject // wants to be in the InheritanceContext tree. if (newEntryNeedsContext) { // ProvideSelfAsInheritanceContext no-ops null, non-DO values, etc. ProvideSelfAsInheritanceContext(newEntry.LocalValue, dp); } // DANGER: Callout might add/remove entries in the effective value table. // Uncomment the following if you need to use entryIndex post // context hookup. // // entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } return result; }
private EffectiveValueEntry EvaluateExpression( EntryIndex entryIndex, DependencyProperty dp, Expression expr, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry newEntry) { object value = expr.GetValue(this, dp); bool isDeferredReference = false; if (value != DependencyProperty.UnsetValue && value != Expression.NoValue) { isDeferredReference = (value is DeferredReference); if (!isDeferredReference && !dp.IsValidValue(value)) { #region EventTracing #if VERBOSE_PROPERTY_EVENT if (isDynamicTracing) { if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.verbose)) { EventTrace.EventProvider.TraceEvent(EventTrace.PROPERTYGUID, MS.Utility.EventType.EndEvent, EventTrace.PROPERTYVALIDATION, 0xFFF ); } } #endif #endregion EventTracing throw new InvalidOperationException(SR.Get(SRID.InvalidPropertyValue, value, dp.Name)); } } else { if (value == Expression.NoValue) { // The expression wants to "hide". First set the // expression value to NoValue to indicate "hiding". newEntry.SetExpressionValue(Expression.NoValue, expr); // Next, get the expression value some other way. if (!dp.ReadOnly) { EvaluateBaseValueCore(dp, metadata, ref newEntry); value = newEntry.GetFlattenedEntry(RequestFlags.FullyResolved).Value; } else { value = DependencyProperty.UnsetValue; } } // if there is still no value, use the default if (value == DependencyProperty.UnsetValue) { value = metadata.GetDefaultValue(this, dp); } } // Set the expr and its evaluated value into // the _effectiveValues cache newEntry.SetExpressionValue(value, expr); return newEntry; }
// // This method // 1. Retrieves an instance value from per-instance StyleData. // 2. Creates the StyleData if this is the first request. // internal static object GetInstanceValue( UncommonField<HybridDictionary []> dataField, DependencyObject container, FrameworkElement feChild, FrameworkContentElement fceChild, int childIndex, DependencyProperty dp, int i, ref EffectiveValueEntry entry) { object rawValue = entry.Value; DependencyObject child = null; FrameworkElement feContainer; FrameworkContentElement fceContainer; Helper.DowncastToFEorFCE(container, out feContainer, out fceContainer, true); HybridDictionary[] styleData = (dataField != null) ? dataField.GetValue(container) : null; HybridDictionary instanceValues = (styleData != null) ? styleData[(int)InstanceStyleData.InstanceValues] : null; InstanceValueKey key = new InstanceValueKey(childIndex, dp.GlobalIndex, i); object value = (instanceValues != null)? instanceValues[key] : null; bool isRequestingExpression = (feChild != null) ? feChild.IsRequestingExpression : fceChild.IsRequestingExpression; if (value == null) { value = NotYetApplied; } // if the value is a detached expression, replace it with a new one Expression expr = value as Expression; if (expr != null && expr.HasBeenDetached) { value = NotYetApplied; } // if this is the first request, create the value if (value == NotYetApplied) { child = feChild; if (child == null) child = fceChild; MarkupExtension me; Freezable freezable; if ((me = rawValue as MarkupExtension) != null) { // exception: if the child is not yet initialized and the request // is for an expression, don't create the value. This gives the parser // a chance to set local values, to override the style-defined values. if (isRequestingExpression) { bool isInitialized = (feChild != null) ? feChild.IsInitialized : fceChild.IsInitialized; if (!isInitialized) { return DependencyProperty.UnsetValue; } } ProvideValueServiceProvider provideValueServiceProvider = new ProvideValueServiceProvider(); provideValueServiceProvider.SetData( child, dp ); value = me.ProvideValue(provideValueServiceProvider); } else if ((freezable = rawValue as Freezable) != null) { value = freezable.Clone(); child.ProvideSelfAsInheritanceContext(value, dp); } // store it in per-instance StyleData (even if it's DependencyProperty.UnsetValue) Debug.Assert(value != NotYetApplied, "attempt to retrieve instance value that was never set"); instanceValues[key] = value; if (value != DependencyProperty.UnsetValue) { expr = value as Expression; // if the instance value is an expression, attach it if (expr != null) { expr.OnAttach(child, dp); } } } // if the value is an Expression (and we're being asked for the real value), // delegate to the expression. if (expr != null) { if (!isRequestingExpression) { if (child == null) { child = feChild; if (child == null) child = fceChild; } entry.ResetValue(DependencyObject.ExpressionInAlternativeStore, true); entry.SetExpressionValue(expr.GetValue(child, dp), DependencyObject.ExpressionInAlternativeStore); } else { entry.Value = value; } } else { entry.Value = value; } return value; }