/// <summary> /// Removes / un-registers a definition instance. /// </summary> /// <remarks> /// If the collection of registered definitions becomes empty /// instantiates self removal from owner's collection. /// </remarks> internal void RemoveMember(DefinitionBase member) { Invalidate(); _registry.Remove(member); if (_registry.Count == 0) { _sharedSizeScope.Remove(_sharedSizeGroupId); } }
internal DistributionOrderIndexComparer(DefinitionBase[] definitions) { Invariant.Assert(definitions != null); this.definitions = definitions; }
/// <summary> /// Adds / registers a definition instance. /// </summary> internal void AddMember(DefinitionBase member) { Debug.Assert(!_registry.Contains(member)); _registry.Add(member); Invalidate(); }
/// <summary> /// Calculates and sets final size for all definitions in the given array. /// </summary> /// <param name="definitions">Array of definitions to process.</param> /// <param name="finalSize">Final size to lay out to.</param> /// <param name="rows">True if sizing row definitions, false for columns</param> private void SetFinalSize( DefinitionBase[] definitions, double finalSize, bool columns) { int starDefinitionsCount = 0; // traverses form the first entry up int nonStarIndex = definitions.Length; // traverses from the last entry down double allPreferredArrangeSize = 0; bool useLayoutRounding = this.UseLayoutRounding; int[] definitionIndices = DefinitionIndices; double[] roundingErrors = null; // If using layout rounding, check whether rounding needs to compensate for high DPI double dpi = 1.0; if (useLayoutRounding) { dpi = columns ? FrameworkElement.DpiScaleX : FrameworkElement.DpiScaleY; roundingErrors = RoundingErrors; } for (int i = 0; i < definitions.Length; ++i) { // if definition is shared then is cannot be star Debug.Assert(!definitions[i].IsShared || !definitions[i].UserSize.IsStar); if (definitions[i].UserSize.IsStar) { double starValue = definitions[i].UserSize.Value; if (_IsZero(starValue)) { // cach normilized star value temporary into MeasureSize definitions[i].MeasureSize = 0; definitions[i].SizeCache = 0; } else { // clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values // can be summed up without overflow starValue = Math.Min(starValue, c_starClip); // Note: normalized star value is temporary cached into MeasureSize definitions[i].MeasureSize = starValue; double maxSize = Math.Max(definitions[i].MinSizeForArrange, definitions[i].UserMaxSize); maxSize = Math.Min(maxSize, c_starClip); definitions[i].SizeCache = maxSize / starValue; if (useLayoutRounding) { roundingErrors[i] = definitions[i].SizeCache; definitions[i].SizeCache = UIElement.RoundLayoutValue(definitions[i].SizeCache, dpi); } } definitionIndices[starDefinitionsCount++] = i; } else { double userSize = 0; switch (definitions[i].UserSize.GridUnitType) { case (GridUnitType.Pixel): userSize = definitions[i].UserSize.Value; break; case (GridUnitType.Auto): userSize = definitions[i].MinSizeForArrange; break; } double userMaxSize; if (definitions[i].IsShared) { // overriding userMaxSize effectively prevents squishy-ness. // this is a "solution" to avoid shared definitions from been sized to // different final size at arrange time, if / when different grids receive // different final sizes. userMaxSize = userSize; } else { userMaxSize = definitions[i].UserMaxSize; } definitions[i].SizeCache = Math.Max(definitions[i].MinSizeForArrange, Math.Min(userSize, userMaxSize)); if (useLayoutRounding) { roundingErrors[i] = definitions[i].SizeCache; definitions[i].SizeCache = UIElement.RoundLayoutValue(definitions[i].SizeCache, dpi); } allPreferredArrangeSize += definitions[i].SizeCache; definitionIndices[--nonStarIndex] = i; } } // indices should meet Debug.Assert(nonStarIndex == starDefinitionsCount); if (starDefinitionsCount > 0) { StarDistributionOrderIndexComparer starDistributionOrderIndexComparer = new StarDistributionOrderIndexComparer(definitions); Array.Sort(definitionIndices, 0, starDefinitionsCount, starDistributionOrderIndexComparer); // the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow... // partial sum value is stored in each definition's SizeCache member. // this way the algorithm guarantees (starValue <= definition.SizeCache) and thus // (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero. // this is an important change from previous implementation where the following was possible: // ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0... double allStarWeights = 0; int i = starDefinitionsCount - 1; do { allStarWeights += definitions[definitionIndices[i]].MeasureSize; definitions[definitionIndices[i]].SizeCache = allStarWeights; } while (--i >= 0); i = 0; do { double resolvedSize; double starValue = definitions[definitionIndices[i]].MeasureSize; if (_IsZero(starValue)) { resolvedSize = definitions[definitionIndices[i]].MinSizeForArrange; } else { double userSize = Math.Max(finalSize - allPreferredArrangeSize, 0.0) * (starValue / definitions[definitionIndices[i]].SizeCache); resolvedSize = Math.Min(userSize, definitions[definitionIndices[i]].UserMaxSize); resolvedSize = Math.Max(definitions[definitionIndices[i]].MinSizeForArrange, resolvedSize); } definitions[definitionIndices[i]].SizeCache = resolvedSize; if (useLayoutRounding) { roundingErrors[definitionIndices[i]] = definitions[definitionIndices[i]].SizeCache; definitions[definitionIndices[i]].SizeCache = UIElement.RoundLayoutValue(definitions[definitionIndices[i]].SizeCache, dpi); } allPreferredArrangeSize += definitions[definitionIndices[i]].SizeCache; } while (++i < starDefinitionsCount); } if ( allPreferredArrangeSize > finalSize && !_AreClose(allPreferredArrangeSize, finalSize) ) { DistributionOrderIndexComparer distributionOrderIndexComparer = new DistributionOrderIndexComparer(definitions); Array.Sort(definitionIndices, 0, definitions.Length, distributionOrderIndexComparer); double sizeToDistribute = finalSize - allPreferredArrangeSize; for (int i = 0; i < definitions.Length; ++i) { int definitionIndex = definitionIndices[i]; double final = definitions[definitionIndex].SizeCache + (sizeToDistribute / (definitions.Length - i)); double finalOld = final; final = Math.Max(final, definitions[definitionIndex].MinSizeForArrange); final = Math.Min(final, definitions[definitionIndex].SizeCache); if (useLayoutRounding) { roundingErrors[definitionIndex] = final; final = UIElement.RoundLayoutValue(finalOld, dpi); final = Math.Max(final, definitions[definitionIndex].MinSizeForArrange); final = Math.Min(final, definitions[definitionIndex].SizeCache); } sizeToDistribute -= (final - definitions[definitionIndex].SizeCache); definitions[definitionIndex].SizeCache = final; } allPreferredArrangeSize = finalSize - sizeToDistribute; } if (useLayoutRounding) { if (!_AreClose(allPreferredArrangeSize, finalSize)) { // Compute deltas for (int i = 0; i < definitions.Length; ++i) { roundingErrors[i] = roundingErrors[i] - definitions[i].SizeCache; definitionIndices[i] = i; } // Sort rounding errors RoundingErrorIndexComparer roundingErrorIndexComparer = new RoundingErrorIndexComparer(roundingErrors); Array.Sort(definitionIndices, 0, definitions.Length, roundingErrorIndexComparer); double adjustedSize = allPreferredArrangeSize; double dpiIncrement = UIElement.RoundLayoutValue(1.0, dpi); if (allPreferredArrangeSize > finalSize) { int i = definitions.Length - 1; while ((adjustedSize > finalSize && !_AreClose(adjustedSize, finalSize)) && i >= 0) { DefinitionBase definition = definitions[definitionIndices[i]]; double final = definition.SizeCache - dpiIncrement; final = Math.Max(final, definition.MinSizeForArrange); if (final < definition.SizeCache) { adjustedSize -= dpiIncrement; } definition.SizeCache = final; i--; } } else if (allPreferredArrangeSize < finalSize) { int i = 0; while ((adjustedSize < finalSize && !_AreClose(adjustedSize, finalSize)) && i < definitions.Length) { DefinitionBase definition = definitions[definitionIndices[i]]; double final = definition.SizeCache + dpiIncrement; final = Math.Max(final, definition.MinSizeForArrange); if (final > definition.SizeCache) { adjustedSize += dpiIncrement; } definition.SizeCache = final; i++; } } } } definitions[0].FinalOffset = 0.0; for (int i = 0; i < definitions.Length; ++i) { definitions[(i + 1) % definitions.Length].FinalOffset = definitions[i].FinalOffset + definitions[i].SizeCache; } }
/// <summary> /// Calculates final (aka arrange) size for given range. /// </summary> /// <param name="definitions">Array of definitions to process.</param> /// <param name="start">Start of the range.</param> /// <param name="count">Number of items in the range.</param> /// <returns>Final size.</returns> private double GetFinalSizeForRange( DefinitionBase[] definitions, int start, int count) { double size = 0; int i = start + count - 1; do { size += definitions[i].SizeCache; } while (--i >= start); return (size); }
/// <summary> /// Resolves Star's for given array of definitions. /// </summary> /// <param name="definitions">Array of definitions to resolve stars.</param> /// <param name="availableSize">All available size.</param> /// <remarks> /// Must initialize LayoutSize for all Star entries in given array of definitions. /// </remarks> private void ResolveStar( DefinitionBase[] definitions, double availableSize) { DefinitionBase[] tempDefinitions = TempDefinitions; int starDefinitionsCount = 0; double takenSize = 0; for (int i = 0; i < definitions.Length; ++i) { switch (definitions[i].SizeType) { case (LayoutTimeSizeType.Auto): takenSize += definitions[i].MinSize; break; case (LayoutTimeSizeType.Pixel): takenSize += definitions[i].MeasureSize; break; case (LayoutTimeSizeType.Star): { tempDefinitions[starDefinitionsCount++] = definitions[i]; double starValue = definitions[i].UserSize.Value; if (_IsZero(starValue)) { definitions[i].MeasureSize = 0; definitions[i].SizeCache = 0; } else { // clipping by c_starClip guarantees that sum of even a very big number of max'ed out star values // can be summed up without overflow starValue = Math.Min(starValue, c_starClip); // Note: normalized star value is temporary cached into MeasureSize definitions[i].MeasureSize = starValue; double maxSize = Math.Max(definitions[i].MinSize, definitions[i].UserMaxSize); maxSize = Math.Min(maxSize, c_starClip); definitions[i].SizeCache = maxSize / starValue; } } break; } } if (starDefinitionsCount > 0) { Array.Sort(tempDefinitions, 0, starDefinitionsCount, s_starDistributionOrderComparer); // the 'do {} while' loop below calculates sum of star weights in order to avoid fp overflow... // partial sum value is stored in each definition's SizeCache member. // this way the algorithm guarantees (starValue <= definition.SizeCache) and thus // (starValue / definition.SizeCache) will never overflow due to sum of star weights becoming zero. // this is an important change from previous implementation where the following was possible: // ((BigValueStar + SmallValueStar) - BigValueStar) resulting in 0... double allStarWeights = 0; int i = starDefinitionsCount - 1; do { allStarWeights += tempDefinitions[i].MeasureSize; tempDefinitions[i].SizeCache = allStarWeights; } while (--i >= 0); i = 0; do { double resolvedSize; double starValue = tempDefinitions[i].MeasureSize; if (_IsZero(starValue)) { resolvedSize = tempDefinitions[i].MinSize; } else { double userSize = Math.Max(availableSize - takenSize, 0.0) * (starValue / tempDefinitions[i].SizeCache); resolvedSize = Math.Min(userSize, tempDefinitions[i].UserMaxSize); resolvedSize = Math.Max(tempDefinitions[i].MinSize, resolvedSize); } tempDefinitions[i].MeasureSize = resolvedSize; takenSize += resolvedSize; } while (++i < starDefinitionsCount); } }
/// <summary> /// Calculates desired size for given array of definitions. /// </summary> /// <param name="definitions">Array of definitions to use for calculations.</param> /// <returns>Desired size.</returns> private double CalculateDesiredSize( DefinitionBase[] definitions) { double desiredSize = 0; for (int i = 0; i < definitions.Length; ++i) { desiredSize += definitions[i].MinSize; } return (desiredSize); }
/// <summary> /// Accumulates length type information for given definition's range. /// </summary> /// <param name="definitions">Source array of definitions to read values from.</param> /// <param name="start">Starting index of the range.</param> /// <param name="count">Number of definitions included in the range.</param> /// <returns>Length type for given range.</returns> private LayoutTimeSizeType GetLengthTypeForRange( DefinitionBase[] definitions, int start, int count) { Debug.Assert(0 < count && 0 <= start && (start + count) <= definitions.Length); LayoutTimeSizeType lengthType = LayoutTimeSizeType.None; int i = start + count - 1; do { lengthType |= definitions[i].SizeType; } while (--i >= start); return (lengthType); }
/// <summary> /// Distributes min size back to definition array's range. /// </summary> /// <param name="start">Start of the range.</param> /// <param name="count">Number of items in the range.</param> /// <param name="requestedSize">Minimum size that should "fit" into the definitions range.</param> /// <param name="definitions">Definition array receiving distribution.</param> /// <param name="percentReferenceSize">Size used to resolve percentages.</param> private void EnsureMinSizeInDefinitionRange( DefinitionBase[] definitions, int start, int count, double requestedSize, double percentReferenceSize) { Debug.Assert(1 < count && 0 <= start && (start + count) <= definitions.Length); // avoid processing when asked to distribute "0" if (!_IsZero(requestedSize)) { DefinitionBase[] tempDefinitions = TempDefinitions; // temp array used to remember definitions for sorting int end = start + count; int autoDefinitionsCount = 0; double rangeMinSize = 0; double rangePreferredSize = 0; double rangeMaxSize = 0; double maxMaxSize = 0; // maximum of maximum sizes // first accumulate the necessary information: // a) sum up the sizes in the range; // b) count the number of auto definitions in the range; // c) initialize temp array // d) cache the maximum size into SizeCache // e) accumulate max of max sizes for (int i = start; i < end; ++i) { double minSize = definitions[i].MinSize; double preferredSize = definitions[i].PreferredSize; double maxSize = Math.Max(definitions[i].UserMaxSize, minSize); rangeMinSize += minSize; rangePreferredSize += preferredSize; rangeMaxSize += maxSize; definitions[i].SizeCache = maxSize; // sanity check: no matter what, but min size must always be the smaller; // max size must be the biggest; and preferred should be in between Debug.Assert( minSize <= preferredSize && preferredSize <= maxSize && rangeMinSize <= rangePreferredSize && rangePreferredSize <= rangeMaxSize ); if (maxMaxSize < maxSize) maxMaxSize = maxSize; if (definitions[i].UserSize.IsAuto) autoDefinitionsCount++; tempDefinitions[i - start] = definitions[i]; } // avoid processing if the range already big enough if (requestedSize > rangeMinSize) { if (requestedSize <= rangePreferredSize) { // // requestedSize fits into preferred size of the range. // distribute according to the following logic: // * do not distribute into auto definitions - they should continue to stay "tight"; // * for all non-auto definitions distribute to equi-size min sizes, without exceeding preferred size. // // in order to achieve that, definitions are sorted in a way that all auto definitions // are first, then definitions follow ascending order with PreferredSize as the key of sorting. // double sizeToDistribute; int i; Array.Sort(tempDefinitions, 0, count, s_spanPreferredDistributionOrderComparer); for (i = 0, sizeToDistribute = requestedSize; i < autoDefinitionsCount; ++i) { // sanity check: only auto definitions allowed in this loop Debug.Assert(tempDefinitions[i].UserSize.IsAuto); // adjust sizeToDistribute value by subtracting auto definition min size sizeToDistribute -= (tempDefinitions[i].MinSize); } for (; i < count; ++i) { // sanity check: no auto definitions allowed in this loop Debug.Assert(!tempDefinitions[i].UserSize.IsAuto); double newMinSize = Math.Min(sizeToDistribute / (count - i), tempDefinitions[i].PreferredSize); if (newMinSize > tempDefinitions[i].MinSize) { tempDefinitions[i].UpdateMinSize(newMinSize); } sizeToDistribute -= newMinSize; } // sanity check: requested size must all be distributed Debug.Assert(_IsZero(sizeToDistribute)); } else if (requestedSize <= rangeMaxSize) { // // requestedSize bigger than preferred size, but fit into max size of the range. // distribute according to the following logic: // * do not distribute into auto definitions, if possible - they should continue to stay "tight"; // * for all non-auto definitions distribute to euqi-size min sizes, without exceeding max size. // // in order to achieve that, definitions are sorted in a way that all non-auto definitions // are last, then definitions follow ascending order with MaxSize as the key of sorting. // double sizeToDistribute; int i; Array.Sort(tempDefinitions, 0, count, s_spanMaxDistributionOrderComparer); for (i = 0, sizeToDistribute = requestedSize - rangePreferredSize; i < count - autoDefinitionsCount; ++i) { // sanity check: no auto definitions allowed in this loop Debug.Assert(!tempDefinitions[i].UserSize.IsAuto); double preferredSize = tempDefinitions[i].PreferredSize; double newMinSize = preferredSize + sizeToDistribute / (count - autoDefinitionsCount - i); tempDefinitions[i].UpdateMinSize(Math.Min(newMinSize, tempDefinitions[i].SizeCache)); sizeToDistribute -= (tempDefinitions[i].MinSize - preferredSize); } for (; i < count; ++i) { // sanity check: only auto definitions allowed in this loop Debug.Assert(tempDefinitions[i].UserSize.IsAuto); double preferredSize = tempDefinitions[i].MinSize; double newMinSize = preferredSize + sizeToDistribute / (count - i); tempDefinitions[i].UpdateMinSize(Math.Min(newMinSize, tempDefinitions[i].SizeCache)); sizeToDistribute -= (tempDefinitions[i].MinSize - preferredSize); } // sanity check: requested size must all be distributed Debug.Assert(_IsZero(sizeToDistribute)); } else { // // requestedSize bigger than max size of the range. // distribute according to the following logic: // * for all definitions distribute to equi-size min sizes. // double equalSize = requestedSize / count; if ( equalSize < maxMaxSize && !_AreClose(equalSize, maxMaxSize) ) { // equi-size is less than maximum of maxSizes. // in this case distribute so that smaller definitions grow faster than // bigger ones. double totalRemainingSize = maxMaxSize * count - rangeMaxSize; double sizeToDistribute = requestedSize - rangeMaxSize; // sanity check: totalRemainingSize and sizeToDistribute must be real positive numbers Debug.Assert( !double.IsInfinity(totalRemainingSize) && !DoubleUtil.IsNaN(totalRemainingSize) && totalRemainingSize > 0 && !double.IsInfinity(sizeToDistribute) && !DoubleUtil.IsNaN(sizeToDistribute) && sizeToDistribute > 0 ); for (int i = 0; i < count; ++i) { double deltaSize = (maxMaxSize - tempDefinitions[i].SizeCache) * sizeToDistribute / totalRemainingSize; tempDefinitions[i].UpdateMinSize(tempDefinitions[i].SizeCache + deltaSize); } } else { // // equi-size is greater or equal to maximum of max sizes. // all definitions receive equalSize as their mim sizes. // for (int i = 0; i < count; ++i) { tempDefinitions[i].UpdateMinSize(equalSize); } } } } } }
/// <summary> /// Validates layout time size type information on given array of definitions. /// Sets MinSize and MeasureSizes. /// </summary> /// <param name="definitions">Array of definitions to update.</param> /// <param name="treatStarAsAuto">if "true" then star definitions are treated as Auto.</param> private void ValidateDefinitionsLayout( DefinitionBase[] definitions, bool treatStarAsAuto) { for (int i = 0; i < definitions.Length; ++i) { definitions[i].OnBeforeLayout(this); double userMinSize = definitions[i].UserMinSize; double userMaxSize = definitions[i].UserMaxSize; double userSize = 0; switch (definitions[i].UserSize.GridUnitType) { case (GridUnitType.Pixel): definitions[i].SizeType = LayoutTimeSizeType.Pixel; userSize = definitions[i].UserSize.Value; // this was brought with NewLayout and defeats squishy behavior userMinSize = Math.Max(userMinSize, Math.Min(userSize, userMaxSize)); break; case (GridUnitType.Auto): definitions[i].SizeType = LayoutTimeSizeType.Auto; userSize = double.PositiveInfinity; break; case (GridUnitType.Star): if (treatStarAsAuto) { definitions[i].SizeType = LayoutTimeSizeType.Auto; userSize = double.PositiveInfinity; } else { definitions[i].SizeType = LayoutTimeSizeType.Star; userSize = double.PositiveInfinity; } break; default: Debug.Assert(false); break; } definitions[i].UpdateMinSize(userMinSize); definitions[i].MeasureSize = Math.Max(userMinSize, Math.Min(userSize, userMaxSize)); } }
/// <summary> /// Calculates one dimensional measure size for given definitions' range. /// </summary> /// <param name="definitions">Source array of definitions to read values from.</param> /// <param name="start">Starting index of the range.</param> /// <param name="count">Number of definitions included in the range.</param> /// <returns>Calculated measure size.</returns> /// <remarks> /// For "Auto" definitions MinWidth is used in place of PreferredSize. /// </remarks> private double GetMeasureSizeForRange( DefinitionBase[] definitions, int start, int count) { Debug.Assert(0 < count && 0 <= start && (start + count) <= definitions.Length); double measureSize = 0; int i = start + count - 1; do { measureSize += (definitions[i].SizeType == LayoutTimeSizeType.Auto) ? definitions[i].MinSize : definitions[i].MeasureSize; } while (--i >= start); return (measureSize); }
// Gets Column or Row definition at index from grid based on resize direction private static void SetDefinitionLength(DefinitionBase definition, GridLength length) { definition.SetValue(definition is ColumnDefinition ? ColumnDefinition.WidthProperty : RowDefinition.HeightProperty, length); }
// Retrieves the ActualWidth or ActualHeight of the definition depending on its type Column or Row private double GetActualLength(DefinitionBase definition) { ColumnDefinition column = definition as ColumnDefinition; return column == null ? ((RowDefinition)definition).ActualHeight : column.ActualWidth; }
// These methods are to help abstract dealing with rows and columns. // DefinitionBase already has internal helpers for getting Width/Height, MinWidth/MinHeight, and MaxWidth/MaxHeight // Returns true if the row/column has a Star length private static bool IsStar(DefinitionBase definition) { return definition.UserSizeValueCache.IsStar; }
/// <summary> /// OnLayoutUpdated handler. Validates that all participating definitions /// have updated min size value. Forces another layout update cycle if needed. /// </summary> private void OnLayoutUpdated(object sender, EventArgs e) { double sharedMinSize = 0; // accumulate min size of all participating definitions for (int i = 0, count = _registry.Count; i < count; ++i) { sharedMinSize = Math.Max(sharedMinSize, _registry[i]._minSize); } bool sharedMinSizeChanged = !DoubleUtil.AreClose(_minSize, sharedMinSize); // compare accumulated min size with min sizes of the individual definitions for (int i = 0, count = _registry.Count; i < count; ++i) { DefinitionBase definitionBase = _registry[i]; // we'll set d.UseSharedMinimum to maintain the invariant: // d.UseSharedMinimum iff d._minSize < this.MinSize // i.e. iff d is not a "long-pole" definition. // // Measure/Arrange of d's Grid uses d._minSize for long-pole // definitions, and max(d._minSize, shared size) for // short-pole definitions. This distinction allows us to react // to changes in "long-pole-ness" more efficiently and correctly, // by avoiding remeasures when a long-pole definition changes. bool useSharedMinimum = !DoubleUtil.AreClose(definitionBase._minSize, sharedMinSize); // before doing that, determine whether d's Grid needs to be remeasured. // It's important _not_ to remeasure if the last measure is still // valid, otherwise infinite loops are possible bool measureIsValid; if (!definitionBase.UseSharedMinimum) { // d was a long-pole. measure is valid iff it's still a long-pole, // since previous measure didn't use shared size. measureIsValid = !useSharedMinimum; } else if (useSharedMinimum) { // d was a short-pole, and still is. measure is valid // iff the shared size didn't change measureIsValid = !sharedMinSizeChanged; } else { // d was a short-pole, but is now a long-pole. This can // happen in several ways: // a. d's minSize increased to or past the old shared size // b. other long-pole definitions decreased, leaving // d as the new winner // In the former case, the measure is valid - it used // d's new larger minSize. In the latter case, the // measure is invalid - it used the old shared size, // which is larger than d's (possibly changed) minSize measureIsValid = (definitionBase.LayoutWasUpdated && DoubleUtil.GreaterThanOrClose(definitionBase._minSize, this.MinSize)); } if (!measureIsValid) { Grid parentGrid = (Grid)definitionBase.Parent; parentGrid.InvalidateMeasure(); } else if (!DoubleUtil.AreClose(sharedMinSize, definitionBase.SizeCache)) { // if measure is valid then also need to check arrange. // Note: definitionBase.SizeCache is volatile but at this point // it contains up-to-date final size Grid parentGrid = (Grid)definitionBase.Parent; parentGrid.InvalidateArrange(); } // now we can restore the invariant, and clear the layout flag definitionBase.UseSharedMinimum = useSharedMinimum; definitionBase.LayoutWasUpdated = false; } _minSize = sharedMinSize; _layoutUpdatedHost.LayoutUpdated -= _layoutUpdated; _layoutUpdatedHost = null; _broadcastInvalidation = true; }
/// <summary> /// Adds / registers a definition instance. /// </summary> internal void AddMember(DefinitionBase member) { Debug.Assert(!_registry.Contains(member)); _registry.Add(member); Invalidate(); }