/// <summary> /// The common code shared by all variants of SetValue /// </summary> // 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, bool coerceWithCurrentValue, 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); } // if there's an expression in some other store, fetch it now Expression currentExpr = (oldEntry.HasExpressionMarker) ? _getExpressionCore(this, dp, metadata) : (oldEntry.IsExpression) ? (oldEntry.LocalValue as Expression) : null; // Allow expression to store value if new value is // not an Expression, if applicable 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); } // CALLBACK handled = currentExpr.SetValue(this, dp, value); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } // Create the new effective value entry EffectiveValueEntry newEntry; if (handled) { // If expression handled set, then done 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)); } coerceWithCurrentValue = false; // expression already handled the control-value } else { // allow a control-value to coerce an expression value, when the // expression didn't handle the value if (coerceWithCurrentValue && currentExpr != null) { currentExpr = null; } newEntry = new EffectiveValueEntry(dp, BaseValueSourceInternal.Local); // detach the old expression, if applicable if (currentExpr != null) { // CALLBACK DependencySource[] currentSources = currentExpr.GetSources(); UpdateSourceDependentLists(this, dp, currentSources, currentExpr, false); // Remove // CALLBACK currentExpr.OnDetach(this, dp); currentExpr.MarkDetached(); entryIndex = CheckEntryIndex(entryIndex, dp.GlobalIndex); } // attach the new expression, if applicable if (newExpr == null) { // simple local value set newEntry.HasExpressionMarker = newValueHasExpressionMarker; newEntry.Value = value; } else { Debug.Assert(!coerceWithCurrentValue, "Expression values not supported in SetCurrentValue"); // 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, coerceWithCurrentValue, operationType); }