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)); } } } }
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); } } } }
// // 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 } }
/// <summary> /// List of sources of the Expression /// </summary> /// <returns>Sources list</returns> internal override DependencySource[] GetSources() { int j, k; int n = (_sources != null) ? _sources.Length : 0; if (n == 0) return null; DependencySource[] result = new DependencySource[n]; // convert the weak references into strong ones for (j=0, k=0; k<n; ++k) { DependencyObject d = _sources[k].DependencyObject; if (d != null) { result[j++] = new DependencySource(d, _sources[k].DependencyProperty); } } // if any of the references died, return a shortened list if (j < n) { DependencySource[] temp = result; result = new DependencySource[j]; Array.Copy(temp, 0, result, 0, j); } return result; }
// change WeakDependencySources to (strong) DependencySources, and notify // the property engine about the new sources void ChangeSources(DependencyObject target, DependencyProperty dp, WeakDependencySource[] newSources) { DependencySource[] sources; if (newSources != null) { // convert weak reference to strong sources = new DependencySource[newSources.Length]; int n = 0; for (int i = 0; i < newSources.Length; ++i) { DependencyObject sourceDO = newSources[i].DependencyObject; if (sourceDO != null) { // only include sources that are still alive sources[n++] = new DependencySource(sourceDO, newSources[i].DependencyProperty); } } // if any of the sources were no longer alive, trim the array if (n < newSources.Length) { DependencySource[] temp; if (n > 0) { temp = new DependencySource[n]; Array.Copy(sources, 0, temp, 0, n); } else { temp = null; } sources = temp; } } else { sources = null; } // notify property engine ChangeSources(target, dp, sources); }
/// <summary> /// Dynamically change this Expression sources (availiable only for NonShareable /// Expressions) /// </summary> /// <remarks> /// Expression must be in use on provided DependencyObject/DependencyProperty. /// GetSources must reflect the old sources to be replaced by the provided newSources. /// </remarks> /// <param name="d">DependencyObject whose sources are to be updated</param> /// <param name="dp">The property that the Expression is set to</param> /// <param name="newSources">New sources</param> internal void ChangeSources(DependencyObject d, DependencyProperty dp, DependencySource[] newSources) { if (d == null && !ForwardsInvalidations) { throw new ArgumentNullException("d"); } if (dp == null && !ForwardsInvalidations) { throw new ArgumentNullException("dp"); } if (Shareable) { throw new InvalidOperationException(SR.Get(SRID.ShareableExpressionsCannotChangeSources)); } DependencyObject.ValidateSources(d, newSources, this); // Additional validation in callee if (ForwardsInvalidations) { DependencyObject.ChangeExpressionSources(this, null, null, newSources); } else { DependencyObject.ChangeExpressionSources(this, d, dp, newSources); } }