示例#1
0
        private void DoMeasure(Size availableSize)
        {
            InvalidateArrange();

            MeasureCore(availableSize);
            LayoutInformation.SetAvailableSize(this, availableSize);
            _isMeasureValid = true;
        }
示例#2
0
        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);
            }
        }
示例#3
0
        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);
示例#4
0
        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;
        }
示例#5
0
    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
                })
示例#6
0
        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;
            }
        }