Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Base / System / Windows / DependencyObject.cs / 2 / DependencyObject.cs
// #define NESTED_OPERATIONS_ using System; using System.Collections; using System.Diagnostics; using System.Globalization; // For CultureInfo.InvariantCulture using System.Reflection; using System.Security.Permissions; // For LinkDemand using System.Windows.Threading; using MS.Utility; using MS.Internal; using MS.Internal.WindowsBase; namespace System.Windows { ////// DependencyObject is an object that participates in the property dependency system /// ////// DependencyObject encompasses all property engine services. It's primary function /// is providing facilities to compute a property's value based on other properties. /// This attribute allows designers looking at metadata through TypeDescriptor to see dependency properties /// and attached properties. [System.ComponentModel.TypeDescriptionProvider(typeof(MS.Internal.ComponentModel.DependencyObjectProvider))] public class DependencyObject : DispatcherObject { ////// /// The Property Engine introduces a new type of property: attached properties. Attached /// properties are identified via and are read and /// written using GetValue and SetValue. /// /// Attached properties may be set and queried on any DependencyObject-derived type. /// /// is used to define relationships between properties. SetValue /// is used to apply the Expression to the property on the instance. /// /// DependencyObject services include the following: /// /// ///
///- Dependency-based property value evaluation through Expressions
///- Property invalidation dependent traversal through Expressions
///- Attached property support
///- Invalidation notification services
////// Default DependencyObject constructor /// ////// Automatic determination of current Dispatcher. Use alternative constructor /// that accepts a Dispatcher for best performance. /// public DependencyObject() { Initialize(); } ///Returns the DType that represents the CLR type of this instance public DependencyObjectType DependencyObjectType { get { if (_dType == null) { // Specialized type identification _dType = DependencyObjectType.FromSystemTypeInternal(GetType()); } // Do not call VerifyAccess because this method is trivial. return _dType; } } private void Initialize() { CanBeInheritanceContext = true; CanModifyEffectiveValues = true; } ////// Makes this object Read-Only state of this object; when in a Read-Only state, SetValue is not permitted, /// though the effective value for a property may change. /// [FriendAccessAllowed] // Built into Base, also used by Framework. internal virtual void Seal() { Debug.Assert(!(this is Freezable), "A Freezable should not call DO's implementation of Seal()"); // Currently DependencyObject.Seal() is semantically different than Freezable.Freeze(). // Though freezing implies sealing the reverse isn't true. The salient difference // here is that sealing a DependencyObject does not force all DPs on that object to // also be sealed. Thus, when we Seal(), we promote all cached values to locally set // so that the user can continue to modify them. Freezable types instead strip off // the promotion handler and freeze the cached default. Note that when / if we make // Seal() == Freeze this code should go away in favor of the behavior used for Freezables. PropertyMetadata.PromoteAllCachedDefaultValues(this); // Since Freeze doesn't call Seal the code below will also be duplicated in Freeze(). // Since this object no longer changes it won't be able to notify dependents DependentListMapField.ClearValue(this); DO_Sealed = true; } ////// Indicates whether or not this object is in a Read-Only state; when in a Read-Only state, SetValue is not permitted, /// though the effective value for a property may change. /// public bool IsSealed { get { return DO_Sealed; } } ////// We override Equals() to seal it to prevent custom Equals() /// implementations. /// /// There are only two scenarios where overriding Equals makes /// sense: /// /// 1. You are a value type (passed by copy). /// 2. You are an immutable reference type (e.g., System.String). /// /// Otherwise you are going to cause problems with keyed and /// some types of sorted datastructures because your values /// can mutate to be equals or not equals while they reside in /// the store (bad news for System.Collections(.Generic)). /// /// Furthermore, defining equality for two DOs is a very slippery /// slope. Are two brushes "equal" if they both paint red? What /// if one is only red this frame because it is animated? What if /// one is databound? What if one is frozen? ...and so on. /// /// Since a DO can never be immutable (attached properties, etc.) /// it makes sense to disallow overriding of Equals. /// public override sealed bool Equals(Object obj) { return base.Equals(obj); } ////// CS0659: Required when overriding Equals(). Overriding /// GetHashCode() is a bad idea for similar reasons. /// public override sealed int GetHashCode() { return base.GetHashCode(); } ////// Retrieve the value of a property /// /// Dependency property ///The computed value public object GetValue(DependencyProperty dp) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); if (dp == null) { throw new ArgumentNullException("dp"); } // Call Forwarded return GetValueEntry( LookupEntry(dp.GlobalIndex), dp, null, RequestFlags.FullyResolved).Value; } ////// This overload of GetValue returns UnsetValue if the property doesn't /// have an entry in the _effectiveValues. This way we will avoid inheriting /// the default value from the parent. /// [FriendAccessAllowed] // Built into Base, also used by Core. internal EffectiveValueEntry GetValueEntry( EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, RequestFlags requests) { EffectiveValueEntry entry; if (dp.ReadOnly) { if (metadata == null) { metadata = dp.GetMetadata(DependencyObjectType); } GetReadOnlyValueCallback getValueCallback = metadata.GetReadOnlyValueCallback; if (getValueCallback != null) { BaseValueSourceInternal valueSource; entry = new EffectiveValueEntry(dp); entry.Value = getValueCallback(this, out valueSource); entry.BaseValueSourceInternal = valueSource; return entry; } } if (entryIndex.Found) { if ((requests & RequestFlags.RawEntry) != 0) { entry = _effectiveValues[entryIndex.Index]; } else { entry = GetEffectiveValue( entryIndex, dp, requests); } } else { entry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Unknown); } if (entry.Value == DependencyProperty.UnsetValue) { if (dp.IsPotentiallyInherited) { if (metadata == null) { metadata = dp.GetMetadata(DependencyObjectType); } if (metadata.IsInherited) { DependencyObject inheritanceParent = InheritanceParent; if (inheritanceParent != null) { entryIndex = inheritanceParent.LookupEntry(dp.GlobalIndex); if (entryIndex.Found) { entry = inheritanceParent.GetEffectiveValue( entryIndex, dp, requests & RequestFlags.DeferredReferences); entry.BaseValueSourceInternal = BaseValueSourceInternal.Inherited; } } } if (entry.Value != DependencyProperty.UnsetValue) { return entry; } } if ((requests & RequestFlags.SkipDefault) == 0) { if (dp.IsPotentiallyUsingDefaultValueFactory) { if (metadata == null) { metadata = dp.GetMetadata(DependencyObjectType); } if (((requests & (RequestFlags.DeferredReferences | RequestFlags.RawEntry)) != 0) && metadata.UsingDefaultValueFactory) { entry.BaseValueSourceInternal = BaseValueSourceInternal.Default; entry.IsDeferredReference = true; entry.Value = new DeferredMutableDefaultReference(metadata, this, dp); return entry; } } else if (!dp.IsDefaultValueChanged) { return EffectiveValueEntry.CreateDefaultValueEntry(dp, dp.DefaultMetadata.DefaultValue); } if (metadata == null) { metadata = dp.GetMetadata(DependencyObjectType); } return EffectiveValueEntry.CreateDefaultValueEntry(dp, metadata.GetDefaultValue(this, dp)); } } return entry; } ////// This overload of GetValue assumes that entryIndex is valid. /// It also does not do the check storage on the InheritanceParent. /// private EffectiveValueEntry GetEffectiveValue( EntryIndex entryIndex, DependencyProperty dp, RequestFlags requests) { EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; EffectiveValueEntry effectiveEntry = entry.GetFlattenedEntry(requests); if (((requests & (RequestFlags.DeferredReferences | RequestFlags.RawEntry)) != 0) || !effectiveEntry.IsDeferredReference) { return effectiveEntry; } if (!entry.HasModifiers) { // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); if (!entry.HasExpressionMarker) { // The value for this property was meant to come from a dictionary // and the creation of that value had been deferred until this // time for better performance. Now is the time to actually instantiate // this value by querying it from the dictionary. Once we have the // value we can actually replace the deferred reference marker // with the actual value. DeferredReference reference = (DeferredReference)entry.Value; object value = reference.GetValue(entry.BaseValueSourceInternal); if (!dp.IsValidValue(value)) { throw new InvalidOperationException(SR.Get(SRID.InvalidPropertyValue, value, dp.Name)); } // Make sure the entryIndex is in [....] after // the inflation of the deferred reference. entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); entry.Value = value; entry.IsDeferredReference = false; _effectiveValues[entryIndex.Index] = entry; return entry; } } else { Debug.Assert(entry.IsExpression && !entry.IsAnimated && !entry.IsCoerced, "the only modified value that can have deferredreferences is an expression"); ModifiedValue modifiedValue = entry.ModifiedValue; Debug.Assert(entry.IsExpression == true); // The value for this property was meant to come from a dictionary // and the creation of that value had been deferred until this // time for better performance. Now is the time to actually instantiate // this value by querying it from the dictionary. Once we have the // value we can actually replace the deferred reference marker // with the actual value. DeferredReference reference = (DeferredReference) modifiedValue.ExpressionValue; object value = reference.GetValue(entry.BaseValueSourceInternal); if (!dp.IsValidValue(value)) { throw new InvalidOperationException(SR.Get(SRID.InvalidPropertyValue, value, dp.Name)); } // Make sure the entryIndex is in [....] after // the inflation of the deferred reference. entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); modifiedValue.ExpressionValue = value; entry.IsDeferredReference = false; _effectiveValues[entryIndex.Index] = entry; effectiveEntry.IsDeferredReference = false; effectiveEntry.Value = value; } return effectiveEntry; } ////// Sets the local value of a property /// /// Dependency property /// New local value public void SetValue(DependencyProperty dp, object value) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); // Cache the metadata object this method needed to get anyway. PropertyMetadata metadata = SetupPropertyChange(dp); // Do standard property set SetValueCommon(dp, value, metadata, false /* coerceWithDeferredReference */, OperationType.Unknown, false /* isInternal */); } ////// Sets the local value of a property /// The purpose of this internal method is to reuse BooleanBoxes when setting boolean value /// /// Dependency property /// New local value [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal void SetValue(DependencyProperty dp, bool value) { SetValue(dp, MS.Internal.KnownBoxes.BooleanBoxes.Box(value)); } ////// Internal version of SetValue that bypasses type check in IsValidValue; /// This is used in property setters /// /// Dependency property /// New local value [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal void SetValueInternal(DependencyProperty dp, object value) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); // Cache the metadata object this method needed to get anyway. PropertyMetadata metadata = SetupPropertyChange(dp); // Do standard property set SetValueCommon(dp, value, metadata, false /* coerceWithDeferredReference */, OperationType.Unknown, true /* isInternal */); } ////// Sets the local value of a property. /// [FriendAccessAllowed] // Built into Base, also used by Framework. internal void SetDeferredValue(DependencyProperty dp, DeferredReference deferredReference) { // Cache the metadata object this method needed to get anyway. PropertyMetadata metadata = SetupPropertyChange(dp); // Do standard property set SetValueCommon(dp, deferredReference, metadata, true /* coerceWithDeferredReference */, OperationType.Unknown, false /* isInternal */); } ////// Sets the local value of a property with a mutable default value. /// [FriendAccessAllowed] // Built into Base, also used by Framework. internal void SetMutableDefaultValue(DependencyProperty dp, object value) { // Cache the metadata object this method needed to get anyway. PropertyMetadata metadata = SetupPropertyChange(dp); // Do standard property set SetValueCommon(dp, value, metadata, false /* coerceWithDeferredReference */, OperationType.ChangeMutableDefaultValue, false /* isInternal */); } ////// Sets the local value of a property /// The purpose of this internal method is to reuse BooleanBoxes when setting boolean value /// /// Dependency property key /// New local value [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal void SetValue(DependencyPropertyKey dp, bool value) { SetValue(dp, MS.Internal.KnownBoxes.BooleanBoxes.Box(value)); } ////// Sets the local value of a property /// public void SetValue(DependencyPropertyKey key, object value) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); DependencyProperty dp; // Cache the metadata object this method needed to get anyway. PropertyMetadata metadata = SetupPropertyChange(key, out dp); // Do standard property set SetValueCommon(dp, value, metadata, false /* coerceWithDeferredReference */, OperationType.Unknown, false /* isInternal */); } ////// Called by SetValue or ClearValue to verify that the property /// can be changed. /// private PropertyMetadata SetupPropertyChange(DependencyProperty dp) { if ( dp != null ) { if ( !dp.ReadOnly ) { // Get type-specific metadata for this property return dp.GetMetadata(DependencyObjectType); } else { throw new InvalidOperationException(SR.Get(SRID.ReadOnlyChangeNotAllowed, dp.Name)); } } else { throw new ArgumentNullException("dp"); } } ////// Called by SetValue or ClearValue to verify that the property /// can be changed. /// private PropertyMetadata SetupPropertyChange(DependencyPropertyKey key, out DependencyProperty dp) { if ( key != null ) { dp = key.DependencyProperty; if ( dp != null ) { dp.VerifyReadOnlyKey(key); // Get type-specific metadata for this property return dp.GetMetadata(DependencyObjectType); } else { throw new ArgumentException(SR.Get(SRID.ReadOnlyKeyNotAuthorized, dp.Name)); } } else { throw new ArgumentNullException("key"); } } ////// The common code shared by all variants of SetValue /// // Takes metadata from caller because most of them have already retrieved it // for their own purposes, avoiding the duplicate GetMetadata call. private void SetValueCommon( DependencyProperty dp, object value, PropertyMetadata metadata, bool coerceWithDeferredReference, OperationType operationType, bool isInternal) { if (IsSealed) { throw new InvalidOperationException(SR.Get(SRID.SetOnReadOnlyObjectNotAllowed, this)); } Expression newExpr = null; DependencySource[] newSources = null; EntryIndex entryIndex = LookupEntry(dp.GlobalIndex); // Treat Unset as a Clear if( value == DependencyProperty.UnsetValue ) { // Parameters should have already been validated, so we call // into the private method to avoid validating again. ClearValueCommon(entryIndex, dp, metadata); return; } // Validate the "value" against the DP. bool isDeferredReference = false; bool newValueHasExpressionMarker = (value == ExpressionInAlternativeStore); // First try to validate the value; only after this validation fails should we // do the more expensive checks (type checks) for the less common scenarios if (!newValueHasExpressionMarker) { bool isValidValue = isInternal ? dp.IsValidValueInternal(value) : dp.IsValidValue(value); // for properties of type "object", we have to always check for expression & deferredreference if (!isValidValue || dp.IsObjectType) { // 2nd most common is expression newExpr = value as Expression; if (newExpr != null) { // For Expressions, perform additional validation // Make sure Expression is "attachable" if (!newExpr.Attachable) { throw new ArgumentException(SR.Get(SRID.SharingNonSharableExpression)); } // Check dispatchers of all Sources // CALLBACK newSources = newExpr.GetSources(); ValidateSources(this, newSources, newExpr); } else { // and least common is DeferredReference isDeferredReference = (value is DeferredReference); if (!isDeferredReference) { if (!isValidValue) { // it's not a valid value & it's not an expression, so throw throw new ArgumentException(SR.Get(SRID.InvalidPropertyValue, value, dp.Name)); } } } } } // Get old value EffectiveValueEntry oldEntry; if (operationType == OperationType.ChangeMutableDefaultValue) { oldEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Default); oldEntry.Value = value; } else { oldEntry = GetValueEntry(entryIndex, dp, metadata, RequestFlags.RawEntry); } // Get current local value object current = oldEntry.LocalValue; // if there's an expression in some other store, fetch it now Expression currentExpr = null; Expression alternativeExpression = null; bool hasExpressionMarker = oldEntry.HasExpressionMarker; if (hasExpressionMarker) { if (newExpr == null) { alternativeExpression = _getExpressionCore(this, dp, metadata); } if (alternativeExpression != null) { current = alternativeExpression; currentExpr = alternativeExpression; } else { // if we're not going to delegate to the alternative expression, // treat the local value as "unset" current = DependencyProperty.UnsetValue; } } else { currentExpr = (oldEntry.IsExpression) ? (current as Expression) : null; } Debug.Assert(!(current is Expression && current is Freezable), "Assuming that Expression and Freezable don't co-derive. If this is not the case, need to re-think"); // Allow expression to store value if new value is // not an Expression, if applicable EffectiveValueEntry newEntry; bool handled = false; if ((currentExpr != null) && (newExpr == null)) { // Resolve deferred references because we haven't modified // the expression code to work with DeferredReference yet. if (isDeferredReference) { value = ((DeferredReference) value).GetValue(BaseValueSourceInternal.Local); isDeferredReference = false; } // CALLBACK handled = currentExpr.SetValue(this, dp, value); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } // If expression handled set, then done if (handled) { if (entryIndex.Found) { newEntry = _effectiveValues[entryIndex.Index]; } else { // the expression.SetValue resulted in this value being removed from the table; // use the default value. newEntry = EffectiveValueEntry.CreateDefaultValueEntry(dp, metadata.GetDefaultValue(this, dp)); } } else { newEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Local); // Inform expression of attachment and old expression // of detachment, if applicable if (currentExpr != null && currentExpr != alternativeExpression) { // CALLBACK DependencySource[] currentSources = currentExpr.GetSources(); UpdateSourceDependentLists(this, dp, currentSources, currentExpr, false); // Remove // CALLBACK currentExpr.OnDetach(this, dp); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } if (newExpr == null) { // simple local value set ... set the value and deferred reference status newEntry.IsDeferredReference = isDeferredReference; newEntry.Value = value; newEntry.HasExpressionMarker = newValueHasExpressionMarker; } else { // First put the expression in the effectivevalueentry table for this object; // this allows the expression to update the value accordingly in OnAttach SetEffectiveValue(entryIndex, dp, dp.GlobalIndex, metadata, newExpr, BaseValueSourceInternal.Local); // Before the expression is attached it has default value object defaultValue = metadata.GetDefaultValue(this, dp); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); SetExpressionValue(entryIndex, defaultValue, newExpr); UpdateSourceDependentLists(this, dp, newSources, newExpr, true); // Add newExpr.MarkAttached(); // CALLBACK newExpr.OnAttach(this, dp); // the attach may have added entries in the effective value table ... // so, update the entryIndex accordingly. entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); newEntry = EvaluateExpression( entryIndex, dp, newExpr, metadata, oldEntry, _effectiveValues[entryIndex.Index]); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } } UpdateEffectiveValue( entryIndex, dp, metadata, oldEntry, ref newEntry, coerceWithDeferredReference, operationType); } // // This is a helper routine to set this DO as the inheritance context of another, // which has been set as a DP value here. // [FriendAccessAllowed] // Built into Base, also used by Core & Framework. internal bool ProvideSelfAsInheritanceContext( object value, DependencyProperty dp ) { DependencyObject doValue = value as DependencyObject; if (doValue != null) { return ProvideSelfAsInheritanceContext(doValue, dp); } else { return false; } } [FriendAccessAllowed] // Built into Base, also used by Core & Framework. internal bool ProvideSelfAsInheritanceContext( DependencyObject doValue, DependencyProperty dp ) { // We have to call Freezable.AddInheritanceContext even if the request // for a new InheritanceContext is not allowed, because Freezable depends // on side-effects from setting the "Freezable context". Freezable's // implementation does its own checks of the conditions omitted here. // if (doValue != null && this.ShouldProvideInheritanceContext(doValue, dp) && (doValue is Freezable || (this.CanBeInheritanceContext && !doValue.IsInheritanceContextSealed) )) { DependencyObject oldInheritanceContext = doValue.InheritanceContext; doValue.AddInheritanceContext(this, dp); // return true if the inheritance context actually changed to the new value return (this == doValue.InheritanceContext && this != oldInheritanceContext); } else { return false; } } // // This is a helper routine to remove this DO as the inheritance context of another. // [FriendAccessAllowed] // Built into Base, also used by Core & Framework. internal bool RemoveSelfAsInheritanceContext( object value, DependencyProperty dp ) { DependencyObject doValue = value as DependencyObject; if (doValue != null) { return RemoveSelfAsInheritanceContext(doValue, dp); } else { return false; } } [FriendAccessAllowed] // Built into Base, also used by Core & Framework. internal bool RemoveSelfAsInheritanceContext( DependencyObject doValue, DependencyProperty dp ) { // We have to call Freezable.RemoveInheritanceContext even if the request // for a new InheritanceContext is not allowed, because Freezable depends // on side-effects from setting the "Freezable context". Freezable's // implementation does its own checks of the conditions omitted here. // if (doValue != null && this.ShouldProvideInheritanceContext(doValue, dp) && (doValue is Freezable || (this.CanBeInheritanceContext && !doValue.IsInheritanceContextSealed) )) { DependencyObject oldInheritanceContext = doValue.InheritanceContext; doValue.RemoveInheritanceContext(this, dp); // return true if the inheritance context actually changed to the new value return (this == oldInheritanceContext && doValue.InheritanceContext != oldInheritanceContext); } else { return false; } } ////// Clears the local value of a property /// /// Dependency property public void ClearValue(DependencyProperty dp) { // Do not allow foreign threads to clear properties. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); // Cache the metadata object this method needed to get anyway. PropertyMetadata metadata = SetupPropertyChange(dp); EntryIndex entryIndex = LookupEntry(dp.GlobalIndex); ClearValueCommon(entryIndex, dp, metadata); } ////// Clears the local value of a property /// public void ClearValue(DependencyPropertyKey key) { // Do not allow foreign threads to clear properties. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); DependencyProperty dp; // Cache the metadata object this method needed to get anyway. PropertyMetadata metadata = SetupPropertyChange(key, out dp); EntryIndex entryIndex = LookupEntry(dp.GlobalIndex); ClearValueCommon(entryIndex, dp, metadata); } ////// The common code shared by all variants of ClearValue /// private void ClearValueCommon(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata) { if (IsSealed) { throw new InvalidOperationException(SR.Get(SRID.ClearOnReadOnlyObjectNotAllowed, this)); } // Get old value EffectiveValueEntry oldEntry = GetValueEntry( entryIndex, dp, metadata, RequestFlags.RawEntry); // Get current local value // (No need to go through read local callback, just checking // for presence of Expression) object current = oldEntry.LocalValue; // Get current expression Expression currentExpr = (oldEntry.IsExpression) ? (current as Expression) : null; // Inform value expression of detachment, if applicable if (currentExpr != null) { // CALLBACK DependencySource[] currentSources = currentExpr.GetSources(); UpdateSourceDependentLists(this, dp, currentSources, currentExpr, false); // Remove // CALLBACK currentExpr.OnDetach(this, dp); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } // valuesource == Local && value == UnsetValue indicates that we are clearing the local value EffectiveValueEntry newEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Local); // Property is now invalid UpdateEffectiveValue( entryIndex, dp, metadata, oldEntry, ref newEntry, false /* coerceWithDeferredReference */, OperationType.Unknown); } ////// This method is called by DependencyObjectPropertyDescriptor to determine /// if a value is set for a given DP. /// internal bool ContainsValue(DependencyProperty dp) { EntryIndex entryIndex = LookupEntry(dp.GlobalIndex); if (!entryIndex.Found) { return false; } object value = _effectiveValues[entryIndex.Index].LocalValue; return !object.ReferenceEquals(value, DependencyProperty.UnsetValue); } // // Changes the sources of an existing Expression // internal static void ChangeExpressionSources(Expression expr, DependencyObject d, DependencyProperty dp, DependencySource[] newSources) { if (!expr.ForwardsInvalidations) { // Get current local value (should be provided Expression) // (No need to go through read local callback, just checking // for presence of Expression) EntryIndex entryIndex = d.LookupEntry(dp.GlobalIndex); if (!entryIndex.Found || (d._effectiveValues[entryIndex.Index].LocalValue != expr)) { throw new ArgumentException(SR.Get(SRID.SourceChangeExpressionMismatch)); } } // Get current sources // CALLBACK DependencySource[] currentSources = expr.GetSources(); // Remove old if (currentSources != null) { UpdateSourceDependentLists(d, dp, currentSources, expr, false); // Remove } // Add new if (newSources != null) { UpdateSourceDependentLists(d, dp, newSources, expr, true); // Add } } ////// Coerce a property value /// /// Dependency property public void CoerceValue(DependencyProperty dp) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); // IsCoerced == true && value == UnsetValue indicates that we need to re-coerce this value EffectiveValueEntry newEntry = new EffectiveValueEntry(dp, FullValueSource.IsCoerced); UpdateEffectiveValue( LookupEntry(dp.GlobalIndex), dp, dp.GetMetadata(DependencyObjectType), new EffectiveValueEntry() /* oldEntry */, ref newEntry, false /* coerceWithDeferredReference */, OperationType.Unknown); } ////// This is to enable some performance-motivated shortcuts in property /// invalidation. When this is called, it means the caller knows the /// value of the property is pointing to the same object instance as /// before, but the meaning has changed because something within that /// object has changed. /// ////// Clients who are unaware of this will still behave correctly, if not /// particularly performant, by assuming that we have a new instance. /// Since invalidation operations are synchronous, we can set a bit /// to maintain this knowledge through the invalidation operation. /// This would be problematic in cross-thread operations, but the only /// time DependencyObject can be used across thread in today's design /// is when it is a Freezable object that has been Frozen. Frozen /// means no more changes, which means no more invalidations. /// /// This is being done as an internal method to enable the performance /// bug fix #1114409. This is candidate for a public API but we can't /// do that kind of work at the moment. /// [FriendAccessAllowed] // Built into Base, also used by Framework. internal void InvalidateSubProperty(DependencyProperty dp) { // when a sub property changes, send a Changed notification with old and new value being the same, and with // IsASubPropertyChange set to true NotifyPropertyChange(new DependencyPropertyChangedEventArgs(dp, dp.GetMetadata(DependencyObjectType), GetValue(dp))); } ////// Notify the current DependencyObject that a "sub-property" /// change has occurred on the given DependencyProperty. /// ////// This does the same work as InvalidateSubProperty, and in addition /// it raise the Freezable.Changed event if the current DependencyObject /// is a Freezable. This method should be called whenever an /// intermediate object is responsible for propagating the Freezable.Changed /// event (i.e. when the Freezable system doesn't propagate the event itself). /// [FriendAccessAllowed] // Built into Base, also used by Framework. internal void NotifySubPropertyChange(DependencyProperty dp) { InvalidateSubProperty(dp); // if the target is a Freezable, call FireChanged to kick off // notifications to the Freezable's parent chain. Freezable freezable = this as Freezable; if (freezable != null) { freezable.FireChanged(); } } ////// Invalidates a property /// /// Dependency property //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 public void InvalidateProperty(DependencyProperty dp) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); if (dp == null) { throw new ArgumentNullException("dp"); } EffectiveValueEntry newEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Unknown); UpdateEffectiveValue( LookupEntry(dp.GlobalIndex), dp, dp.GetMetadata(DependencyObjectType), new EffectiveValueEntry() /* oldEntry */, ref newEntry, false /* coerceWithDeferredReference */, OperationType.Unknown); } // // This method // 1. Re-evaluates the effective value for the given property and fires the property changed notification // 2. When this method is invoked with the coersion flag set to false it means that we will simply // coerce and will not try to re-evaluate the base value for the property // [FriendAccessAllowed] // Declared in Base also used in Framework internal UpdateResult UpdateEffectiveValue( EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, ref EffectiveValueEntry newEntry, bool coerceWithDeferredReference, 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 ); } */ // 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.IsDeferredReference = oldEntry.IsDeferredReference; newEntry.HasExpressionMarker = oldEntry.HasExpressionMarker; if (oldEntry.IsExpression) { newEntry.SetExpressionValue(modifiedValue.ExpressionValue, baseValue); } if (oldEntry.IsAnimated) { newEntry.SetAnimatedValue(modifiedValue.AnimatedValue, baseValue); } } } } // 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, metadata.CoerceValueCallback, coerceWithDeferredReference, 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, dp.DesignerCoerceValueCallback, false /*coerceWithDeferredReference*/, true /*skipBaseValueChecks*/); // Make sure that the call out did not cause a change to entryIndex entryIndex = CheckEntryIndex(entryIndex, targetIndex); } if (newEntry.FullValueSource != (FullValueSource) BaseValueSourceInternal.Default) { Debug.Assert(newEntry.BaseValueSourceInternal != BaseValueSourceInternal.Unknown, "Value source should be known at this point"); if ((newEntry.BaseValueSourceInternal == BaseValueSourceInternal.Inherited) && !IsSelfInheritanceParent) { 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); UpdateResult result = isAValueChange ? UpdateResult.ValueChanged : 0; 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 void ProcessCoerceValue( DependencyProperty dp, PropertyMetadata metadata, ref EntryIndex entryIndex, ref int targetIndex, ref EffectiveValueEntry newEntry, ref EffectiveValueEntry oldEntry, ref object oldValue, object baseValue, CoerceValueCallback coerceValueCallback, bool coerceWithDeferredReference, bool skipBaseValueChecks) { if (newEntry.IsDeferredReference) { Debug.Assert(!newEntry.IsCoerced && !newEntry.IsAnimated, "Coerced or Animated value cannot be a deferred reference"); // Allow values to stay deferred through coercion callbacks in // limited circumstances, when we know the listener is internal. // Since we never assign DeferredReference instances to // non-internal (non-friend assembly) classes, it's safe to skip // the dereference if the callback is to the DP owner (and not // a derived type). This is consistent with passing raw // DeferredReference instances to ValidateValue callbacks, which // only ever go to the owner class. if (!coerceWithDeferredReference || dp.OwnerType != metadata.CoerceValueCallback.Method.DeclaringType) // Need 2nd check to rule out derived class callback overrides. { // Resolve deferred references because we need the actual // baseValue to evaluate the correct animated value. This is done // by invoking GetValue for this property. DeferredReference dr = (DeferredReference) baseValue; baseValue = dr.GetValue(newEntry.BaseValueSourceInternal); // Set the baseValue back into the entry and clear the // IsDeferredReference flag newEntry.SetCoersionBaseValue(baseValue); newEntry.IsDeferredReference = false; entryIndex = CheckEntryIndex(entryIndex, targetIndex); } } object coercedValue = coerceValueCallback(this, baseValue); // Make sure that the call out did not cause a change to entryIndex entryIndex = CheckEntryIndex(entryIndex, targetIndex); if (!Equals(dp, coercedValue, baseValue)) { // returning DependencyProperty.UnsetValue from a Coercion callback means "don't do the set" ... // or "use previous value" if (coercedValue == DependencyProperty.UnsetValue) { if (oldEntry.IsDeferredReference) { DeferredReference reference = (DeferredReference)oldValue; oldValue = reference.GetValue(oldEntry.BaseValueSourceInternal); entryIndex = CheckEntryIndex(entryIndex, targetIndex); } coercedValue = oldValue; } // Note that we do not support the value being coerced to a // DeferredReference if (!dp.IsValidValue(coercedValue)) { throw new ArgumentException(SR.Get(SRID.InvalidPropertyValue, coercedValue, dp.Name)); } // Set the coerced value here. All other values would // have been set during EvaluateEffectiveValue/GetValueCore. newEntry.SetCoercedValue(coercedValue, baseValue, skipBaseValueChecks); } } ////// This is a helper method that is used to fire the property change notification through /// the callbacks and to all the dependents of this property such as bindings etc. /// [FriendAccessAllowed] // Built into Base, also used by Framework. internal void NotifyPropertyChange(DependencyPropertyChangedEventArgs args) { // fire change notification OnPropertyChanged(args); if (args.IsAValueChange || args.IsASubPropertyChange) { // Invalidate all Dependents of this Source invalidation due // to Expression dependencies DependencyProperty dp = args.Property; object objectDependentsListMap = DependentListMapField.GetValue(this); if (objectDependentsListMap != null) { FrugalMap dependentListMap = (FrugalMap)objectDependentsListMap; object dependentList = dependentListMap[dp.GlobalIndex]; Debug.Assert(dependentList != null, "dependentList should either be unset or non-null"); if (dependentList != DependencyProperty.UnsetValue) { // The list can "go empty" if the items it references "went away" if (((DependentList)dependentList).IsEmpty) dependentListMap[dp.GlobalIndex] = DependencyProperty.UnsetValue; else ((DependentList)dependentList).InvalidateDependents(this, args); } } } } 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.IsDeferredReference = isDeferredReference; newEntry.SetExpressionValue(value, expr); return newEntry; } //[CodeAnalysis("AptcaMethodsShouldOnlyCallAptcaMethods")] //Tracking Bug: 29647 private EffectiveValueEntry EvaluateEffectiveValue( EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry newEntry, // this is only used to recognize if this is a clear local value OperationType operationType) { #region EventTracing #if VERBOSE_PROPERTY_EVENT bool isDynamicTracing = EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.verbose); // This was under "normal" if (isDynamicTracing) { ++ValidationCount; if( ValidationCount % 100 == 0 ) { EventTrace.EventProvider.TraceEvent(EventTrace.PROPERTYVALIDATIONGUID, MS.Utility.EventType.Info, ValidationCount ); } string TypeAndName = String.Format(CultureInfo.InvariantCulture, "[{0}]{1}({2})",GetType().Name,dp.Name,base.GetHashCode()); // FxCop wanted the CultureInfo.InvariantCulture EventTrace.EventProvider.TraceEvent(EventTrace.PROPERTYVALIDATIONGUID, 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 validating out of control? if( NestedOperations > NestedOperationMaximum ) { // We're validating out of control, time to abort. throw new InvalidOperationException("Too many levels of nested DependencyProperty GetValue calls. This usually indicates a circular reference in the application and the cycle needs to be broken."); } NestedOperations++; // Decrement in the finally block #endif object value = DependencyProperty.UnsetValue; try { // Read local storage bool isSetValue = (newEntry.BaseValueSourceInternal == BaseValueSourceInternal.Local); bool isClearLocalValue = isSetValue && (newEntry.Value == DependencyProperty.UnsetValue); bool oldLocalIsExpression = false; if (isClearLocalValue) { newEntry.BaseValueSourceInternal = BaseValueSourceInternal.Unknown; } else { // if we reached this on a re-evaluate of a setvalue, we need to make sure // we don't lose track of the newly specified local value. // for all other cases, the oldEntry will have the local value we should // use. value = isSetValue ? newEntry.LocalValue : oldEntry.LocalValue; if (value == ExpressionInAlternativeStore) { value = DependencyProperty.UnsetValue; } else { oldLocalIsExpression = isSetValue ? newEntry.IsExpression : oldEntry.IsExpression; } } // (If local storage not Unset and not an Expression, return) if (value != DependencyProperty.UnsetValue) { newEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Local); newEntry.Value = value; newEntry.IsDeferredReference = value is DeferredReference; // Check if an Expression is set if (oldLocalIsExpression) { // CALLBACK newEntry = EvaluateExpression( entryIndex, dp, (Expression) value, metadata, oldEntry, newEntry); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); value = newEntry.ModifiedValue.ExpressionValue; } } // Subclasses are not allowed to resolve/modify the value for read-only properties. if( !dp.ReadOnly ) { // Give subclasses a chance to resolve/modify the value EvaluateBaseValueCore(dp, metadata, ref newEntry); // we need to have the default value in the entry before we do the animation check if (newEntry.BaseValueSourceInternal == BaseValueSourceInternal.Unknown) { newEntry = EffectiveValueEntry.CreateDefaultValueEntry(dp, metadata.GetDefaultValue(this, dp)); } value = newEntry.GetFlattenedEntry(RequestFlags.FullyResolved).Value; entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); if (oldEntry.IsAnimated) { newEntry.ResetCoercedValue(); EvaluateAnimatedValueCore(dp, metadata, ref newEntry); value = newEntry.GetFlattenedEntry(RequestFlags.FullyResolved).Value; } } } finally { #if NESTED_OPERATIONS_CHECK NestedOperations--; #endif } #region EventTracing #if VERBOSE_PROPERTY_EVENT if (isDynamicTracing) { if (EventTrace.IsEnabled(EventTrace.Flags.performance, EventTrace.Level.verbose)) { int UsingDefault = 1; if (value != DependencyProperty.UnsetValue) UsingDefault = 0; EventTrace.EventProvider.TraceEvent(EventTrace.PROPERTYVALIDATIONGUID, MS.Utility.EventType.EndEvent, UsingDefault); } } #endif #endregion EventTracing if (value == DependencyProperty.UnsetValue) { newEntry = EffectiveValueEntry.CreateDefaultValueEntry(dp, metadata.GetDefaultValue(this, dp)); } return newEntry; } ////// Allows subclasses to participate in property base value computation /// internal virtual void EvaluateBaseValueCore( DependencyProperty dp, PropertyMetadata metadata, ref EffectiveValueEntry newEntry) { } ////// Allows subclasses to participate in property animated value computation /// internal virtual void EvaluateAnimatedValueCore( DependencyProperty dp, PropertyMetadata metadata, ref EffectiveValueEntry newEntry) { } ////// Notification that a specified property has been changed /// /// EventArgs that contains the property, metadata, old value, and new value for this change protected virtual void OnPropertyChanged(DependencyPropertyChangedEventArgs e) { // Do not call VerifyAccess because this is a virtual, and is used as a call-out. if( e.Property == null ) { throw new ArgumentNullException("e.Property"); } if (e.IsAValueChange || e.IsASubPropertyChange || e.OperationType == OperationType.ChangeMutableDefaultValue) { // Inform per-type/property invalidation listener, if exists PropertyMetadata metadata = e.Metadata; if ((metadata != null) && (metadata.PropertyChangedCallback != null)) { metadata.PropertyChangedCallback(this, e); } } } ////// Override this method to control whether a DependencyProperty should be serialized. /// The base implementation returns true if the property is set (locally) on this object. /// protected internal virtual bool ShouldSerializeProperty( DependencyProperty dp ) { return ContainsValue( dp ); } [FriendAccessAllowed] // Built into Base, also used by Core & Framework. internal BaseValueSourceInternal GetValueSource(DependencyProperty dp, PropertyMetadata metadata, out bool hasModifiers) { bool isExpression, isAnimated, isCoerced; return GetValueSource(dp, metadata, out hasModifiers, out isExpression, out isAnimated, out isCoerced); } [FriendAccessAllowed] // Built into Base, also used by Core & Framework. internal BaseValueSourceInternal GetValueSource(DependencyProperty dp, PropertyMetadata metadata, out bool hasModifiers, out bool isExpression, out bool isAnimated, out bool isCoerced) { if (dp == null) { throw new ArgumentNullException("dp"); } EntryIndex entryIndex = LookupEntry(dp.GlobalIndex); if (entryIndex.Found) { EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; hasModifiers = entry.HasModifiers; isExpression = entry.IsExpression; isAnimated = entry.IsAnimated; isCoerced = entry.IsCoerced; return entry.BaseValueSourceInternal; } else { isExpression = false; isAnimated = false; isCoerced = false; if (dp.ReadOnly) { if (metadata == null) { metadata = dp.GetMetadata(DependencyObjectType); } GetReadOnlyValueCallback callback = metadata.GetReadOnlyValueCallback; if (callback != null) { BaseValueSourceInternal source; callback(this, out source); hasModifiers = false; return source; } } if (dp.IsPotentiallyInherited) { if (metadata == null) { metadata = dp.GetMetadata(DependencyObjectType); } if (metadata.IsInherited) { DependencyObject inheritanceParent = InheritanceParent; if (inheritanceParent != null && inheritanceParent.LookupEntry(dp.GlobalIndex).Found) { hasModifiers = false; return BaseValueSourceInternal.Inherited; } } } } hasModifiers = false; return BaseValueSourceInternal.Default; } ////// Retrieve the local value of a property (if set) /// /// Dependency property ////// The local value. DependencyProperty.UnsetValue if no local value was /// set via public object ReadLocalValue(DependencyProperty dp) { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); if (dp == null) { throw new ArgumentNullException("dp"); } EntryIndex entryIndex = LookupEntry(dp.GlobalIndex); // Call Forwarded return ReadLocalValueEntry(entryIndex, dp, false /* allowDeferredReferences */); } ///. /// /// Retrieve the local value of a property (if set) /// ////// The local value. DependencyProperty.UnsetValue if no local value was /// set via internal object ReadLocalValueEntry(EntryIndex entryIndex, DependencyProperty dp, bool allowDeferredReferences) { if (!entryIndex.Found) { return DependencyProperty.UnsetValue; } EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; object value = entry.LocalValue; // convert a deferred reference into a real value if (!allowDeferredReferences && entry.IsDeferredReference) { // localValue may still not be a DeferredReference, e.g. // if it is an expression whose value is a DeferredReference. // So a little more work is needed before converting the value. DeferredReference dr = value as DeferredReference; if (dr != null) { value = dr.GetValue(entry.BaseValueSourceInternal); } } // treat Expression marker as "unset" if (value == ExpressionInAlternativeStore) { value = DependencyProperty.UnsetValue; } return value; } ///. /// /// Create a local value enumerator for this instance /// ///Local value enumerator (stack based) public LocalValueEnumerator GetLocalValueEnumerator() { // Do not allow foreign threads access. // (This is a noop if this object is not assigned to a Dispatcher.) // this.VerifyAccess(); uint effectiveValuesCount = EffectiveValuesCount; LocalValueEntry[] snapshot = new LocalValueEntry[effectiveValuesCount]; int count = 0; // Iterate through the sorted effectiveValues for (uint i=0; i/// This is how we track if someone is enumerating the _effectiveValues /// cache. This flag should be set to false before doing that. /// private bool CanModifyEffectiveValues { get { return (_packedData & 0x00080000) != 0; } set { Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); if (value) { _packedData |= 0x00080000; } else { _packedData &= 0xFFF7FFFF; } } } [FriendAccessAllowed] // defined in Base, used in Core and Framework internal bool IsInheritanceContextSealed { get { return (_packedData & 0x01000000) != 0; } set { if (value) { _packedData |= 0x01000000; } else { _packedData &= 0xFEFFFFFF; } } } private bool DO_Sealed { get { return (_packedData & 0x00400000) != 0; } set { if (value) { _packedData |= 0x00400000; } else { _packedData &= 0xFFBFFFFF; } } } // Freezable State stored here for size optimization: // Freezable is immutable internal bool Freezable_Frozen { // uses the same bit as Sealed ... even though they are not quite synonymous // Since Frozen implies Sealed, and calling Seal() is disallowed on Freezable, // this is ok. get { return DO_Sealed; } set { DO_Sealed = value; } } // Freezable State stored here for size optimization: // Freezable is being referenced in multiple places and hence cannot have a single InheritanceContext internal bool Freezable_HasMultipleInheritanceContexts { get { return (_packedData & 0x02000000) != 0; } set { if (value) { _packedData |= 0x02000000; } else { _packedData &= 0xFDFFFFFF; } } } // Freezable State stored here for size optimization: // Handlers stored in a dictionary internal bool Freezable_UsingHandlerList { get { return (_packedData & 0x04000000) != 0; } set { if (value) { _packedData |= 0x04000000; } else { _packedData &= 0xFBFFFFFF; } } } // Freezable State stored here for size optimization: // Context stored in a dictionary internal bool Freezable_UsingContextList { get { return (_packedData & 0x08000000) != 0; } set { if (value) { _packedData |= 0x08000000; } else { _packedData &= 0xF7FFFFFF; } } } // Freezable State stored here for size optimization: // Freezable has a single handler internal bool Freezable_UsingSingletonHandler { get { return (_packedData & 0x10000000) != 0; } set { if (value) { _packedData |= 0x10000000; } else { _packedData &= 0xEFFFFFFF; } } } // Freezable State stored here for size optimization: // Freezable has a single context internal bool Freezable_UsingSingletonContext { get { return (_packedData & 0x20000000) != 0; } set { if (value) { _packedData |= 0x20000000; } else { _packedData &= 0xDFFFFFFF; } } } // Animatable State stored here for size optimization: // internal bool Animatable_IsResourceInvalidationNecessary { [FriendAccessAllowed] // Built into Base, but used by Core. get { return (_packedData & 0x40000000) != 0; } [FriendAccessAllowed] // Built into Base, but used by Core. set { if (value) { _packedData |= 0x40000000; } else { _packedData &= 0xBFFFFFFF; } } } // IAnimatable State stored here for size optimization: // Returns true if this IAnimatable implemention has animations on its properties // but doesn't check the sub-properties for animations. internal bool IAnimatable_HasAnimatedProperties { [FriendAccessAllowed] // Built into Base, but used by Core. get { return (_packedData & 0x80000000) != 0; } [FriendAccessAllowed] // Built into Base, but used by Core. set { if (value) { _packedData |= 0x80000000; } else { _packedData &= 0x7FFFFFFF; } } } internal static void UpdateSourceDependentLists(DependencyObject d, DependencyProperty dp, DependencySource[] sources, Expression expr, bool add) { // Sources already validated to be on the same thread as Dependent (d) if (sources != null) { // don't hold a reference on the dependent if the expression is doing // the invalidations. This helps avoid memory leaks (bug 871139) if (expr.ForwardsInvalidations) { d = null; dp = null; } for (int i = 0; i < sources.Length; i++) { DependencySource source = sources[i]; // A Sealed DependencyObject does not have a Dependents list // so don't bother updating it (or attempt to add one). Debug.Assert((!source.DependencyObject.IsSealed) || (DependentListMapField.GetValue(source.DependencyObject) == default(object))); if (!source.DependencyObject.IsSealed) { // Retrieve the DependentListMap for this source // The list of dependents to invalidate is stored using a special negative key FrugalMap dependentListMap; object value = DependentListMapField.GetValue(source.DependencyObject); if (value != null) { dependentListMap = (FrugalMap)value; } else { dependentListMap = new FrugalMap(); } // Get list of DependentList off of ID map of Source object dependentListObj = dependentListMap[source.DependencyProperty.GlobalIndex]; Debug.Assert(dependentListObj != null, "dependentList should either be unset or non-null"); // Add/Remove new Dependent (this) to Source's list if (add) { DependentList dependentList; if (dependentListObj == DependencyProperty.UnsetValue) { dependentListMap[source.DependencyProperty.GlobalIndex] = dependentList = new DependentList(); } else { dependentList = (DependentList)dependentListObj; } dependentList.Add(d, dp, expr); } else { if (dependentListObj != DependencyProperty.UnsetValue) { DependentList dependentList = (DependentList)dependentListObj; dependentList.Remove(d, dp, expr); if (dependentList.IsEmpty) { // No more dependencies for this property; reclaim the space if we can. dependentListMap[source.DependencyProperty.GlobalIndex] = DependencyProperty.UnsetValue; } } } // Set the updated struct back into the source's _localStore. DependentListMapField.SetValue(source.DependencyObject, dependentListMap); } } } } internal static void ValidateSources(DependencyObject d, DependencySource[] newSources, Expression expr) { // Make sure all Sources are owned by the same thread. if (newSources != null) { Dispatcher dispatcher = d.Dispatcher; for (int i = 0; i < newSources.Length; i++) { Dispatcher sourceDispatcher = newSources[i].DependencyObject.Dispatcher; if (sourceDispatcher != dispatcher && !(expr.SupportsUnboundSources && sourceDispatcher == null)) { throw new ArgumentException(SR.Get(SRID.SourcesMustBeInSameThread)); } } } } /// /// Register the two callbacks that are used to implement the "alternative /// Expression storage" feature, and return the two methods used to access /// the feature. /// ////// This method should only be called (once) from the Framework. It should /// not be called directly by users. /// [FriendAccessAllowed] // Built into Base, also used by Framework. internal static void RegisterForAlternativeExpressionStorage( AlternativeExpressionStorageCallback getExpressionCore, out AlternativeExpressionStorageCallback getExpression) { Debug.Assert(getExpressionCore != null, "getExpressionCore cannot be null"); Debug.Assert(_getExpressionCore == null, "The 'alternative Expression storage' feature has already been registered"); _getExpressionCore = getExpressionCore; getExpression = new AlternativeExpressionStorageCallback(GetExpression); } ////// Used to determine whether a DependencyObject has a value with an expression, such as a resource reference. /// ////// True if Dependency object has a value with an expression /// internal bool HasAnyExpression() { EffectiveValueEntry[] effectiveValues = EffectiveValues; uint numEffectiveValues = EffectiveValuesCount; bool result = false; for (uint i = 0; i < numEffectiveValues; i++) { DependencyProperty dp = DependencyProperty.RegisteredPropertyList.List[effectiveValues[i].PropertyIndex]; if (dp != null) { EntryIndex entryIndex = new EntryIndex(i); // The expression check only needs to be done when isChecking is true // because if we return false here the Freeze() call will fail. if (HasExpression(entryIndex, dp)) { result = true; break; } } } return result; } ////// Return true iff the property has an expression applied to it. /// [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal bool HasExpression(EntryIndex entryIndex, DependencyProperty dp) { if (!entryIndex.Found) { return false; } EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; object o = entry.LocalValue; bool result = (entry.HasExpressionMarker || o is Expression); return result; } ////// Return the Expression (if any) currently in effect for the given property. /// private static Expression GetExpression(DependencyObject d, DependencyProperty dp, PropertyMetadata metadata) { EntryIndex entryIndex = d.LookupEntry(dp.GlobalIndex); if (!entryIndex.Found) { return null; } EffectiveValueEntry entry = d._effectiveValues[entryIndex.Index]; if (entry.HasExpressionMarker) { if (_getExpressionCore != null) { return _getExpressionCore(d, dp, metadata); } return null; } // no expression marker -- check local value itself if (entry.IsExpression) { return (Expression) entry.LocalValue; } return null; } #region InheritanceContext ////// InheritanceContext /// internal virtual DependencyObject InheritanceContext { [FriendAccessAllowed] // Built into Base, also used by Core and Framework. get { return null; } } ////// You have a new InheritanceContext /// ////// This method is equivalent to OnNewParent of /// the yesteryears on an element. Note that the /// implementation may choose to ignore this new /// context, e.g. in the case of a Freezable that /// is being shared. /// /// Do not call this method directly. Instead call /// ProvideSelfAsInheritanceContext, which checks various /// preconditions and then calls AddInheritanceContext for you. /// internal virtual void AddInheritanceContext(DependencyObject context, DependencyProperty property) { } ////// You have lost an InheritanceContext /// ////// /// Do not call this method directly. Instead call /// RemoveSelfAsInheritanceContext, which checks various /// preconditions and then calls RemoveInheritanceContext for you. /// internal virtual void RemoveInheritanceContext(DependencyObject context, DependencyProperty property) { } ////// You are about to provided as the InheritanceContext for the target. /// You can choose to allow this or not. /// internal virtual bool ShouldProvideInheritanceContext(DependencyObject target, DependencyProperty property) { return true; } ////// The InheritanceContext for an ancestor /// has changed /// ////// This is the equivalent of OnAncestorChanged /// for an element /// [FriendAccessAllowed] // Built into Base, also used by Core. internal void OnInheritanceContextChanged(EventArgs args) { // Fire the event that BindingExpression and // ResourceReferenceExpression will be listening to. EventHandler handlers = InheritanceContextChangedHandlersField.GetValue(this); if (handlers != null) { handlers(this, args); } CanModifyEffectiveValues = false; try { // Notify all those DO that the current instance is a // context for (we will call these inheritanceChildren) about the // change in the context. This is like a recursive tree walk. // Iterate through the sorted effectiveValues uint effectiveValuesCount = EffectiveValuesCount; for (uint i=0; i/// This is a means for subclasses to get notification /// of InheritanceContext changes and then they can do /// their own thing. /// [FriendAccessAllowed] // Built into Base, also used by Core. internal virtual void OnInheritanceContextChangedCore(EventArgs args) { } /// /// Event for InheritanceContextChanged. This is /// the event that BindingExpression and /// ResourceReferenceExpressions will be listening to. /// ////// make this pay-for-play by storing handlers /// in an uncommon field /// internal event EventHandler InheritanceContextChanged { [FriendAccessAllowed] // Built into Base, also used by Framework. add { // Get existing event hanlders EventHandler handlers = InheritanceContextChangedHandlersField.GetValue(this); if (handlers != null) { // combine to a multicast delegate handlers = (EventHandler)Delegate.Combine(handlers, value); } else { handlers = value; } // Set the delegate as an uncommon field InheritanceContextChangedHandlersField.SetValue(this, handlers); } [FriendAccessAllowed] // Built into Base, also used by Framework. remove { // Get existing event hanlders EventHandler handlers = InheritanceContextChangedHandlersField.GetValue(this); if (handlers != null) { // Remove the given handler handlers = (EventHandler)Delegate.Remove(handlers, value); if (handlers == null) { // Clear the value for the uncommon field // cause there are no more handlers InheritanceContextChangedHandlersField.ClearValue(this); } else { // Set the remaining handlers as an uncommon field InheritanceContextChangedHandlersField.SetValue(this, handlers); } } } } ////// By default this is false since it doesn't have a context /// internal virtual bool HasMultipleInheritanceContexts { get { return false; } } ////// By default this is true since every DependencyObject can be an InheritanceContext /// internal bool CanBeInheritanceContext { [FriendAccessAllowed] // Built into Base, also used by Framework. get { return (_packedData & 0x00200000) != 0; } [FriendAccessAllowed] // Built into Base, also used by Framework. set { if (value) { _packedData |= 0x00200000; } else { _packedData &= 0xFFDFFFFF; } } } ////// Debug-only method that asserts that the current DO does not have any /// listeners on its InheritanceContextChanged event. This is used by /// Freezable (frozen Freezables can't have listeners). /// [Conditional ("DEBUG")] internal void Debug_AssertNoInheritanceContextListeners() { Debug.Assert(InheritanceContextChangedHandlersField.GetValue(this) == null, "This object should not have any listeners to its InheritanceContextChanged event"); } // This uncommon field is used to store the handlers for the InheritanceContextChanged event private static readonly UncommonFieldInheritanceContextChangedHandlersField = new UncommonField (); #endregion InheritanceContext #region EffectiveValues // The rest of DependencyObject is its EffectiveValues cache // The cache of effective (aka "computed" aka "resolved") property // values for this DO. If a DP does not have an entry in this array // it means one of two things: // 1) if it's an inheritable property, then its value may come from // this DO's InheritanceParent // 2) if it's not an inheritable property (or this DO's InheritanceParent // doesn't have an entry for this DP either), then the value for // that DP on this DO is the default value. // Otherwise, the DP will have an entry in this array describing the // current value of the DP, where this value came from, and how it // has been modified internal EffectiveValueEntry[] EffectiveValues { [FriendAccessAllowed] // Built into Base, also used by Framework. get { return _effectiveValues; } } // The total number of entries in the above EffectiveValues cache internal uint EffectiveValuesCount { [FriendAccessAllowed] // Built into Base, also used by Framework. get { return _packedData & 0x000003FF; } private set { _packedData = (_packedData & 0xFFFFFC00) | (value & 0x000003FF); } } // The number of entries in the above EffectiveValues cache that // correspond to DPs that are inheritable on this DO; this count // helps us during "tree change" invalidations to know how big // of a "working change list" we have to construct. internal uint InheritableEffectiveValuesCount { [FriendAccessAllowed] // Built into Base, also used by Framework. get { return (_packedData >> 10) & 0x1FF; } set { Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); _packedData = ((value & 0x1FF) << 10) | (_packedData & 0xFFF803FF); } } // This flag indicates whether or not we are in "Property Initialization // Mode". This is an opt-in mode: a DO starts out *not* in Property // Initialization Mode. In this mode, the EffectiveValues cache grows // at a more liberal (2.0) rate. Normally, outside of this mode, the // cache grows at a much stingier (1.2) rate. // Internal customers (currently only UIElement) access this mode // through the BeginPropertyInitialization/EndPropertyInitialization // methods below private bool IsInPropertyInitialization { get { return (_packedData & 0x00800000) != 0; } set { if (value) { _packedData |= 0x00800000; } else { _packedData &= 0xFF7FFFFF; } } } // A DependencyObject calls this method to indicate to the property // system that a bunch of property sets are about to happen; the // property system responds by elevating the growth rate of the // EffectiveValues cache, to speed up initialization by requiring // fewer reallocations [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal void BeginPropertyInitialization() { IsInPropertyInitialization = true; } // A DependencyObject calls this method to indicate to the property // system that it is now done with the bunch of property sets that // accompanied the initialization of this element; the property // system responds by returning the growth rate of the // EffectiveValues cache to its normal rate, and then trimming // the cache to get rid of any excess bloat incurred by the // aggressive growth rate during initialization mode. [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal void EndPropertyInitialization() { IsInPropertyInitialization = false; if (_effectiveValues != null) { uint effectiveValuesCount = EffectiveValuesCount; if (effectiveValuesCount != 0) { uint endLength = effectiveValuesCount; if (((float) endLength / (float) _effectiveValues.Length) < 0.8) { // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); EffectiveValueEntry[] destEntries = new EffectiveValueEntry[endLength]; Array.Copy(_effectiveValues, 0, destEntries, 0, effectiveValuesCount); _effectiveValues = destEntries; } } } } internal DependencyObject InheritanceParent { [FriendAccessAllowed] // Built into Base, also used by Framework. get { if ((_packedData & 0x3E100000) == 0) { return (DependencyObject) _contextStorage; } // return null if this DO has any of the following set: // IsSelfInheritanceParent // Freezable_HasMultipleInheritanceContexts // Freezable_UsingHandlerList // Freezable_UsingContextList // Freezable_UsingSingletonHandler // Freezable_UsingSingletonContext return null; } } private void SetInheritanceParent(DependencyObject newParent) { Debug.Assert((_packedData & 0x3E000000) == 0, "InheritanceParent should not be set in a Freezable, which manages its own inheritance context."); // For thread-safety, sealed DOs can't modify _contextStorage Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); if (_contextStorage != null) { Debug.Assert(!IsSelfInheritanceParent, "If the IsSelfInheritanceParent is set then the InheritanceParent should have been ----d."); _contextStorage = newParent; } else { if (newParent != null) { // Merge all the inheritable properties on the inheritanceParent into the EffectiveValues // store on the current node because someone had set an effective value for an // inheritable property on this node. if (IsSelfInheritanceParent) { MergeInheritableProperties(newParent); } else { _contextStorage = newParent; } } else { // Do nothing because before and after values are both null } } } internal bool IsSelfInheritanceParent { [FriendAccessAllowed] // Built into Base, also used by Framework. get { return (_packedData & 0x00100000) != 0; } } // Currently we only have support for turning this flag on. Once set this flag never goes false after that. [FriendAccessAllowed] // Built into Base, also used by Framework. internal void SetIsSelfInheritanceParent() { // Merge all the inheritable properties on the inheritanceParent into the EffectiveValues // store on the current node because someone tried to set an effective value for an // inheritable property on this node. DependencyObject inheritanceParent = InheritanceParent; if (inheritanceParent != null) { MergeInheritableProperties(inheritanceParent); // Get rid of the InheritanceParent since we won't need it anymore for // having cached all the inheritable properties on self SetInheritanceParent(null); } Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); _packedData |= 0x00100000; } // // This method // 1. Recalculates the InheritanceParent with respect to the given FrameworkParent // 2. Is called from [FE/FCE].OnAncestorChangedInternal // [FriendAccessAllowed] // Built into Base, also used by Framework. internal void SynchronizeInheritanceParent(DependencyObject parent) { // If this flag is true it indicates that all the inheritable properties for this node // are cached on itself and hence we will not need the InheritanceParent pointer at all. if (!this.IsSelfInheritanceParent) { if (parent != null) { if (!parent.IsSelfInheritanceParent) { SetInheritanceParent(parent.InheritanceParent); } else { SetInheritanceParent(parent); } } else { SetInheritanceParent(null); } } } // // This method // 1. Merges the inheritable properties from the parent into the EffectiveValues store on self // private void MergeInheritableProperties(DependencyObject inheritanceParent) { Debug.Assert(inheritanceParent != null, "Must have inheritanceParent"); Debug.Assert(inheritanceParent.IsSelfInheritanceParent, "An inheritanceParent should always be one that has all the inheritable properties cached on self"); EffectiveValueEntry[] parentEffectiveValues = inheritanceParent.EffectiveValues; uint parentEffectiveValuesCount = inheritanceParent.EffectiveValuesCount; for (uint i=0; i 0 && _effectiveValues.Length > entryIndex.Index) { EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; if (entry.PropertyIndex == targetIndex) { return new EntryIndex(entryIndex.Index); } } return LookupEntry(targetIndex); } // look for an entry that matches the given dp // return value has Found set to true if an entry is found // return value has Index set to the index of the found entry (if Found is true) // or the location to insert an entry for this dp (if Found is false) [FriendAccessAllowed] // Built into Base, also used by Framework. internal EntryIndex LookupEntry(int targetIndex) { int checkIndex; uint iLo = 0; uint iHi = EffectiveValuesCount; if (iHi <= 0) { return new EntryIndex(0, false /* Found */); } // Do a binary search to find the value while (iHi - iLo > 3) { uint iPv = (iHi + iLo) / 2; checkIndex = _effectiveValues[iPv].PropertyIndex; if (targetIndex == checkIndex) { return new EntryIndex(iPv); } if (targetIndex <= checkIndex) { iHi = iPv; } else { iLo = iPv + 1; } } // Now we only have three values to search; switch to a linear search do { checkIndex = _effectiveValues[iLo].PropertyIndex; if (checkIndex == targetIndex) { return new EntryIndex(iLo); } if (checkIndex > targetIndex) { // we've gone past the targetIndex - return not found break; } iLo++; } while (iLo < iHi); return new EntryIndex(iLo, false /* Found */); } // insert the given entry at the given index // this function assumes that entryIndex is at the right // location such that the resulting list remains sorted by EffectiveValueEntry.PropertyIndex private void InsertEntry(EffectiveValueEntry entry, uint entryIndex) { // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); #if DEBUG EntryIndex debugIndex = LookupEntry(entry.PropertyIndex); Debug.Assert(!debugIndex.Found && debugIndex.Index == entryIndex, "Inserting duplicate"); #endif if (CanModifyEffectiveValues == false) { throw new InvalidOperationException(SR.Get(SRID.LocalValueEnumerationInvalidated)); } uint effectiveValuesCount = EffectiveValuesCount; if (effectiveValuesCount > 0) { if (_effectiveValues.Length == effectiveValuesCount) { int newSize = (int) (effectiveValuesCount * (IsInPropertyInitialization ? 2.0 : 1.2)); if (newSize == effectiveValuesCount) { newSize++; } EffectiveValueEntry[] destEntries = new EffectiveValueEntry[newSize]; Array.Copy(_effectiveValues, 0, destEntries, 0, entryIndex); destEntries[entryIndex] = entry; Array.Copy(_effectiveValues, entryIndex, destEntries, entryIndex + 1, effectiveValuesCount - entryIndex); _effectiveValues = destEntries; } else { Array.Copy(_effectiveValues, entryIndex, _effectiveValues, entryIndex + 1, effectiveValuesCount - entryIndex); _effectiveValues[entryIndex] = entry; } } else { if (_effectiveValues == null) { _effectiveValues = new EffectiveValueEntry[EffectiveValuesInitialSize]; } _effectiveValues[0] = entry; } EffectiveValuesCount = effectiveValuesCount + 1; } // remove the entry at the given index private void RemoveEntry(uint entryIndex, DependencyProperty dp) { // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); if (CanModifyEffectiveValues == false) { throw new InvalidOperationException(SR.Get(SRID.LocalValueEnumerationInvalidated)); } uint effectiveValuesCount = EffectiveValuesCount; Array.Copy(_effectiveValues, entryIndex + 1, _effectiveValues, entryIndex, (effectiveValuesCount - entryIndex) - 1); effectiveValuesCount--; EffectiveValuesCount = effectiveValuesCount; // clear last entry _effectiveValues[effectiveValuesCount].Clear(); } // // This property // 1. Finds the correct initial size for the _effectiveValues store on the current DependencyObject // 2. This is a performance optimization // [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal virtual int EffectiveValuesInitialSize { get { return 2; } } internal void SetEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry newEntry, EffectiveValueEntry oldEntry) { if (metadata != null && metadata.IsInherited && newEntry.BaseValueSourceInternal != BaseValueSourceInternal.Inherited && !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.IsExpression) { baseValue = newEntry.ModifiedValue.BaseValue; } else { baseValue = newEntry.ModifiedValue.ExpressionValue; } } 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"); } // // This method // 1. Create a new EffectiveValueEntry for the given DP and inserts it into the EffectiveValues list // [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal void SetEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, int targetIndex, PropertyMetadata metadata, object value, BaseValueSourceInternal valueSource) { Debug.Assert(value != DependencyProperty.UnsetValue, "Value to be set cannot be UnsetValue"); Debug.Assert(valueSource != BaseValueSourceInternal.Unknown, "ValueSource cannot be Unknown"); // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); if (metadata != null && metadata.IsInherited && valueSource != BaseValueSourceInternal.Inherited && !IsSelfInheritanceParent) { SetIsSelfInheritanceParent(); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } EffectiveValueEntry entry; if (entryIndex.Found) { entry = _effectiveValues[entryIndex.Index]; } else { entry = new EffectiveValueEntry(); entry.PropertyIndex = targetIndex; InsertEntry(entry, entryIndex.Index); if (metadata != null && metadata.IsInherited) { InheritableEffectiveValuesCount++; } } bool hasExpressionMarker = (value == ExpressionInAlternativeStore); if (!hasExpressionMarker && entry.HasExpressionMarker && (valueSource == BaseValueSourceInternal.ThemeStyle || valueSource == BaseValueSourceInternal.ThemeStyleTrigger || valueSource == BaseValueSourceInternal.Style || valueSource == BaseValueSourceInternal.TemplateTrigger || valueSource == BaseValueSourceInternal.StyleTrigger || valueSource == BaseValueSourceInternal.ParentTemplate || valueSource == BaseValueSourceInternal.ParentTemplateTrigger)) { entry.BaseValueSourceInternal = valueSource; entry.IsDeferredReference = false; entry.SetExpressionValue(value, ExpressionInAlternativeStore); entry.ResetAnimatedValue(); entry.ResetCoercedValue(); } else if (entry.IsExpression && entry.ModifiedValue.ExpressionValue == Expression.NoValue) { // we now have a value for an expression that is "hiding" - save it // as the expression value entry.SetExpressionValue(value, entry.ModifiedValue.BaseValue); } else { Debug.Assert(entry.BaseValueSourceInternal != BaseValueSourceInternal.Local || valueSource == BaseValueSourceInternal.Local, "No one but another local value can stomp over an existing local value. The only way is to clear the entry"); entry.BaseValueSourceInternal = valueSource; entry.ResetValue(value, hasExpressionMarker); // These are the only possible ValueSources when we could have a // DeferredDictionaryReference set as the entry's value. if (valueSource != BaseValueSourceInternal.Default) { entry.IsDeferredReference = (value is DeferredReference); } else { entry.IsDeferredReference = false; } } Debug.Assert(dp == null || (dp.GlobalIndex == entry.PropertyIndex), "EffectiveValueEntry & DependencyProperty do not match"); _effectiveValues[entryIndex.Index] = entry; } // // This method // 1. Removes the entry if there is one with valueSource >= the specified // internal void UnsetEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata) { if (entryIndex.Found) { RemoveEntry(entryIndex.Index, dp); if (metadata != null && metadata.IsInherited) { InheritableEffectiveValuesCount--; } } } // // This method // 1. Sets the expression on a ModifiedValue entry // private void SetExpressionValue(EntryIndex entryIndex, object value, object baseValue) { Debug.Assert(value != DependencyProperty.UnsetValue, "Value to be set cannot be UnsetValue"); Debug.Assert(baseValue != DependencyProperty.UnsetValue, "BaseValue to be set cannot be UnsetValue"); Debug.Assert(entryIndex.Found == true, "The baseValue for the expression should have been inserted prior to this and hence there should already been an entry for it."); // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; // Deferred reference is possible only in the case of ResourceReferenceExpression // not when the expression originates from an alternate store. if (baseValue != ExpressionInAlternativeStore) { entry.IsDeferredReference = (value is DeferredReference); } else { entry.IsDeferredReference = false; } entry.SetExpressionValue(value, baseValue); entry.ResetAnimatedValue(); entry.ResetCoercedValue(); _effectiveValues[entryIndex.Index] = entry; } // // This method // 1. Sets the animated value on a ModifiedValue entry // [FriendAccessAllowed] // Built into Base, also used by Core and Framework. internal void SetAnimatedValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, object value, object baseValue) { Debug.Assert(value != DependencyProperty.UnsetValue, "Value to be set cannot be UnsetValue"); // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); if (baseValue == DependencyProperty.UnsetValue) { object defaultValue = metadata.GetDefaultValue(this, dp); baseValue = defaultValue; if (!entryIndex.Found || _effectiveValues[entryIndex.Index].BaseValueSourceInternal == BaseValueSourceInternal.Unknown) { // If the default value has been animated we need to make a new entry for it SetEffectiveValue(entryIndex, dp, dp.GlobalIndex, metadata, baseValue, BaseValueSourceInternal.Default); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } } else { Debug.Assert(entryIndex.Found || (!IsSelfInheritanceParent && metadata.IsInherited), "The baseValue for the animation should have been inserted prior to this and hence there should already been an entry for it." + " The only exception to this rule is when an inherited property is not cached on this node but is being held on a parent node."); if (!entryIndex.Found && !IsSelfInheritanceParent && metadata.IsInherited) { // Now is the time to cache all the inheritable values on the current // node because one of them is animated and requires caching SetIsSelfInheritanceParent(); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } } EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; entry.SetAnimatedValue(value, baseValue); entry.ResetCoercedValue(); _effectiveValues[entryIndex.Index] = entry; } // // This method // 1. Sets the coerced value on a ModifiedValue entry // private EffectiveValueEntry SetCoercedValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, object value, object baseValue) { Debug.Assert(value != DependencyProperty.UnsetValue, "Value to be set cannot be UnsetValue"); Debug.Assert(baseValue != DependencyProperty.UnsetValue, "BaseValue to be set cannot be UnsetValue"); // For thread-safety, sealed DOs can't modify _effectiveValues. Debug.Assert(!DO_Sealed, "A Sealed DO cannot be modified"); object defaultValue = metadata.GetDefaultValue(this, dp); if (Equals(dp, baseValue, defaultValue)) { if (!entryIndex.Found || _effectiveValues[entryIndex.Index].BaseValueSourceInternal == BaseValueSourceInternal.Unknown) { // If the default value has been coerced we need to make a new entry for it SetEffectiveValue(entryIndex, dp, dp.GlobalIndex, metadata, defaultValue, BaseValueSourceInternal.Default); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } } else { Debug.Assert(entryIndex.Found || (!IsSelfInheritanceParent && metadata.IsInherited), "The baseValue for the coersion should have been inserted prior to this and hence there should already been an entry for it." + " The only exception to this rule is when an inherited property is not cached on this node but is being held on a parent node."); if (!entryIndex.Found && !IsSelfInheritanceParent && metadata.IsInherited) { // Now is the time to cache all the inheritable values on the current // node because one of them is coerced and requires caching SetIsSelfInheritanceParent(); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } } EffectiveValueEntry entry = _effectiveValues[entryIndex.Index]; entry.SetCoercedValue(value, baseValue, false /*skipBaseValueChecks*/); _effectiveValues[entryIndex.Index] = entry; return entry; } /// /// Helper method to compare two DP values /// private bool Equals(DependencyProperty dp, object value1, object value2) { if (dp.IsValueType || dp.IsStringType) { // Use Object.Equals for Strings and ValueTypes return Object.Equals(value1, value2); } else { // Use Object.ReferenceEquals for all other ReferenceTypes return Object.ReferenceEquals(value1, value2); } } #endregion EffectiveValues #region InstanceData // Specialized Type identification private DependencyObjectType _dType; // For Freezable: // To save working set this object will initially reference a // single delegate/context. If a second object is added // of the same type, we will convert to a list/list, which will // be stored in _contextStorage. If the user ever adds an object of the // other type, we will create a HandlerContextStorage class, which _contextStorage // will then point at. // For FrameworkContentElement/FrameworkElement: // This is the parent whose effective values store would contain the // value for the inheritable property on you. This change part of the // performance optimization around inheritable properties whereby you // wouldn't store the inheritable property on each and every node but // will hold it only the node that the property was actually set. internal object _contextStorage; // The cache of effective values for this DependencyObject // This is an array sorted by DP.GlobalIndex. This ordering is // maintained via an insertion sort algorithm. private EffectiveValueEntry[] _effectiveValues; // Stores: // Bits 0- 9 (0x000003FF): EffectiveValuesCount (0-1023) // Bits 10-18 (0x0007FC00): InheritableEffectiveValuesCount (0-511) // Bit 19 (0x00080000): CanModifyEffectiveValues, says if you can change the _effectiveValues cache on the current element. // Bit 20 (0x00100000): IsSelfInheritanceParent, says if all your inheritable property values are built into your effectiveValues store // Bit 21 (0x00200000): CanBeInheritanceContext, says if you can be an InheritanceContext for someone // Bit 22 (0x00400000): IsSealed: whether or not this DO is in readonly mode // Bit 23 (0x00800000): PropertyInitialization mode // Bit 24 (0x01000000): IsInheritanceContextSealed, says if you can change InheritanceContext // Bit 25 (0x02000000): Freezable_HasMultipleInheritanceContexts // Bit 26 (0x04000000): Freezable_UsingHandlerList // Bit 27 (0x08000000): Freezable_UsingContextList // Bit 28 (0x10000000): Freezable_UsingSingletonHandler // Bit 29 (0x20000000): Freezable_UsingSingletonContext // Bit 30 (0x40000000): Animatable_IsResourceInvalidationNecessary // Bit 31 (0x80000000): Animatable_HasAnimatedProperties private UInt32 _packedData = 0; #endregion InstanceData #region StaticData // special value in local store meaning that some alternative store (e.g. // the Framework's per-instance StyleData) is holding an Expression to // which we want to delegate SetValue. [FriendAccessAllowed] // Built into Base, also used by Framework. internal static readonly object ExpressionInAlternativeStore = new NamedObject("ExpressionInAlternativeStore"); // callbacks used for alternative expression storage private static AlternativeExpressionStorageCallback _getExpressionCore; #if VERBOSE_PROPERTY_EVENT internal static int ValidationCount; internal static int InvalidationCount; #endif // This field stores the list of dependents in a FrugalMap. // The field is of type object for two reasons: // 1) FrugalMap is a struct, and generics over value types have perf issues // 2) so that we can have the default value of "null" mean Unset. internal static readonly UncommonField
Link Menu
![Network programming in C#, Network Programming in VB.NET, Network Programming in .NET](/images/book.jpg)
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- WebMessageEncoderFactory.cs
- DbConnectionPool.cs
- XmlAnyAttributeAttribute.cs
- FloaterBaseParaClient.cs
- RectangleGeometry.cs
- BlurBitmapEffect.cs
- ElementHostPropertyMap.cs
- MethodBuilderInstantiation.cs
- ComUdtElement.cs
- SqlInternalConnectionTds.cs
- CodeMemberEvent.cs
- IOThreadScheduler.cs
- ConfigurationSettings.cs
- FunctionUpdateCommand.cs
- PrePrepareMethodAttribute.cs
- PostBackTrigger.cs
- QilChoice.cs
- NamedPipeProcessProtocolHandler.cs
- CompileXomlTask.cs
- BatchParser.cs
- ManifestSignatureInformation.cs
- AnnotationResourceChangedEventArgs.cs
- SqlDataReaderSmi.cs
- ISAPIWorkerRequest.cs
- TypeElement.cs
- ClientType.cs
- ToolStripRenderer.cs
- Cursors.cs
- HttpListenerResponse.cs
- StringDictionary.cs
- SendingRequestEventArgs.cs
- MostlySingletonList.cs
- SafeNativeMethods.cs
- AnnotationComponentChooser.cs
- XmlDataProvider.cs
- ListBoxItemAutomationPeer.cs
- ConfigurationException.cs
- PropertyTabAttribute.cs
- CombinedHttpChannel.cs
- SafeMILHandle.cs
- RelationshipDetailsRow.cs
- PointUtil.cs
- DesigntimeLicenseContext.cs
- DataContractSerializer.cs
- CodeBlockBuilder.cs
- Win32PrintDialog.cs
- ListControlConvertEventArgs.cs
- GlobalAllocSafeHandle.cs
- AsymmetricKeyExchangeFormatter.cs
- BufferedStream.cs
- Panel.cs
- EdmScalarPropertyAttribute.cs
- ZipIORawDataFileBlock.cs
- MetadataArtifactLoaderComposite.cs
- TreeView.cs
- WebDisplayNameAttribute.cs
- ResourceDescriptionAttribute.cs
- AssociationEndMember.cs
- BasicExpressionVisitor.cs
- Normalization.cs
- dtdvalidator.cs
- PeerInvitationResponse.cs
- ScriptReferenceBase.cs
- Margins.cs
- CodeDirectiveCollection.cs
- SafeCryptoHandles.cs
- ChangeNode.cs
- KeyEvent.cs
- DynamicAttribute.cs
- OledbConnectionStringbuilder.cs
- CodeGenerator.cs
- ADMembershipProvider.cs
- XmlSerializationReader.cs
- CipherData.cs
- Stroke2.cs
- LockCookie.cs
- ScriptDescriptor.cs
- SimpleMailWebEventProvider.cs
- DBSqlParserColumn.cs
- SchemaDeclBase.cs
- FusionWrap.cs
- mactripleDES.cs
- ConstraintCollection.cs
- XmlSchemaCompilationSettings.cs
- EndPoint.cs
- InheritedPropertyChangedEventArgs.cs
- CodeDelegateInvokeExpression.cs
- XmlReaderSettings.cs
- WizardStepBase.cs
- Focus.cs
- MouseActionConverter.cs
- FaultReason.cs
- XmlSchemaCompilationSettings.cs
- FloaterParagraph.cs
- TreeNodeBindingCollection.cs
- DataKey.cs
- SystemResourceHost.cs
- HtmlTableRowCollection.cs
- ImmutableAssemblyCacheEntry.cs
- XmlStringTable.cs