private void DoMeasure(Size availableSize) { InvalidateArrange(); MeasureCore(availableSize); LayoutInformation.SetAvailableSize(this, availableSize); _isMeasureValid = true; }
public void Measure(Size availableSize) { if (!(this is FrameworkElement)) { return; } if (double.IsNaN(availableSize.Width) || double.IsNaN(availableSize.Height)) { throw new InvalidOperationException($"Cannot measure [{GetType()}] with NaN"); } var isCloseToPreviousMeasure = availableSize == LastAvailableSize; if (Visibility == Visibility.Collapsed) { if (!isCloseToPreviousMeasure) { _isMeasureValid = false; LayoutInformation.SetAvailableSize(this, availableSize); } return; } if (_isMeasureValid && isCloseToPreviousMeasure) { return; } if (IsVisualTreeRoot) { try { _isLayoutingVisualTreeRoot = true; DoMeasure(availableSize); } finally { _isLayoutingVisualTreeRoot = false; } } else { // If possible we avoid the try/finally which might be costly on some platforms DoMeasure(availableSize); } }
public void When_UpdateLayout_Then_TreeNotMeasuredUsingCachedValue() { if (Window.Current.RootElement is Panel root) { var sut = new Grid { HorizontalAlignment = HorizontalAlignment.Stretch, VerticalAlignment = VerticalAlignment.Stretch }; var originalRootAvailableSize = LayoutInformation.GetAvailableSize(root); var originalRootDesiredSize = LayoutInformation.GetDesiredSize(root); var originalRootLayoutSlot = LayoutInformation.GetLayoutSlot(root); Size availableSize; Rect layoutSlot; try { LayoutInformation.SetAvailableSize(root, default); LayoutInformation.SetDesiredSize(root, default); LayoutInformation.SetLayoutSlot(root, default); root.Children.Add(sut); sut.UpdateLayout(); availableSize = LayoutInformation.GetAvailableSize(sut); layoutSlot = LayoutInformation.GetLayoutSlot(sut); } finally { LayoutInformation.SetAvailableSize(root, originalRootAvailableSize); LayoutInformation.SetDesiredSize(root, originalRootDesiredSize); LayoutInformation.SetLayoutSlot(root, originalRootLayoutSlot); root.Children.Remove(sut); try { root.UpdateLayout(); } catch { } // Make sure to restore visual tree if test has failed! } Assert.AreNotEqual(default, availableSize);
public void Measure(Size availableSize) { if (!(this is FrameworkElement)) { return; } if (double.IsNaN(availableSize.Width) || double.IsNaN(availableSize.Height)) { throw new InvalidOperationException($"Cannot measure [{GetType()}] with NaN"); } var isCloseToPreviousMeasure = availableSize == LastAvailableSize; if (Visibility == Visibility.Collapsed) { if (!isCloseToPreviousMeasure) { _isMeasureValid = false; LayoutInformation.SetAvailableSize(this, availableSize); } return; } if (_isMeasureValid && isCloseToPreviousMeasure) { return; } InvalidateArrange(); MeasureCore(availableSize); LayoutInformation.SetAvailableSize(this, availableSize); _isMeasureValid = true; }
internal static bool DoMeasure(this ILayouterElement element, Size availableSize, out Size measuredSizeLogical) { var isFirstMeasure = !element.IsFirstMeasureDoneAndManagedElement; // "isDirty" here means this element's MeasureOverride // method NEEDS to be called. var isDirty = isFirstMeasure || // first time here since attached to parent (availableSize != element.LastAvailableSize) || // size changed element.IsMeasureDirty || // .InvalidateMeasure() called !FeatureConfiguration.UIElement.UseInvalidateMeasurePath || // dirty_path disabled globally element.IsMeasureDirtyPathDisabled; // dirty_path disabled locally var frameworkElement = element as FrameworkElement; if (frameworkElement is null) // native unmanaged element? { isDirty = true; } else if (!isDirty) { if (!frameworkElement.IsMeasureDirtyPath) { // That's a weird case, but we need to return something meaningful. measuredSizeLogical = frameworkElement.DesiredSize; return(false); } if (element.GetParent() is not UIElement and not null) { // If the parent if this element is not managed (UIElement), // .MeasureOverride() needs to be called. isDirty = true; } } if (isFirstMeasure) { frameworkElement?.SetLayoutFlags(UIElement.LayoutFlag.FirstMeasureDone); } var remainingTries = UIElement.MaxLayoutIterations; measuredSizeLogical = default; while (--remainingTries > 0) { if (isDirty || frameworkElement is null) { // We must reset the flag **BEFORE** doing the actual measure, so the elements are able to re-invalidate themselves frameworkElement?.ClearLayoutFlags(UIElement.LayoutFlag.MeasureDirty); // The dirty flag is explicitly set on this element try { measuredSizeLogical = element.Layouter.Measure(availableSize); } catch (Exception e) { Application.Current.RaiseRecoverableUnhandledExceptionOrLog(e, element); return(false); } finally { LayoutInformation.SetAvailableSize(element, availableSize); } return(true); // end of isDirty processing } // The measure dirty flag is set on one of the descendents: // it will bypass the current element's .MeasureOverride() // since it shouldn't produce a different result and it's // just a waste of precious CPU time to call it. using var children = frameworkElement.GetChildren().GetEnumerator(); while (children.MoveNext()) { var child = children.Current; // If the child is dirty (or is a path to a dirty descendant child), // We're remeasuring it. if (child is UIElement { IsMeasureOrMeasureDirtyPath : true })
private void DoMeasure(Size availableSize) { var isFirstMeasure = !IsLayoutFlagSet(LayoutFlag.FirstMeasureDone); var isDirty = isFirstMeasure || (availableSize != LastAvailableSize) || IsMeasureDirty || !FeatureConfiguration.UIElement.UseInvalidateMeasurePath || // dirty_path disabled globally IsMeasureDirtyPathDisabled; var isMeasureDirtyPath = IsMeasureDirtyPath; if (!isDirty && !isMeasureDirtyPath) { return; // Nothing to do } if (isFirstMeasure) { SetLayoutFlags(LayoutFlag.FirstMeasureDone); } var remainingTries = MaxLayoutIterations; while (--remainingTries > 0) { if (isDirty) { // We must reset the flag **BEFORE** doing the actual measure, so the elements are able to re-invalidate themselves ClearLayoutFlags(LayoutFlag.MeasureDirty | LayoutFlag.MeasureDirtyPath); // The dirty flag is explicitly set on this element #if DEBUG try #endif { MeasureCore(availableSize); InvalidateArrange(); } #if DEBUG catch (Exception ex) { _log.Error($"Error measuring {this}", ex); throw; } finally #endif { LayoutInformation.SetAvailableSize(this, availableSize); } break; } // isMeasureDirtyPath is always true here ClearLayoutFlags(LayoutFlag.MeasureDirtyPath); // The dirty flag is set on one of the descendents: // it will bypass the current element's MeasureOverride() // since it shouldn't produce a different result and it's // just a waste of precious CPU time to call it. var children = GetChildren().GetEnumerator(); //foreach (var child in children) while (children.MoveNext()) { if (children.Current is { IsMeasureOrMeasureDirtyPath : true } child) { // If the child is dirty (or is a path to a dirty descendant child), // We're remeasuring it. var previousDesiredSize = child.DesiredSize; child.Measure(child.LastAvailableSize); if (child.DesiredSize != previousDesiredSize) { isDirty = true; break; } } } children.Dispose(); // no "using" operator here to prevent an implicit try-catch on Wasm if (isDirty) { continue; } break; } }