Ermanator
Ermanator

Reputation: 17

WPF UpdateLayout() Not Updating ScrollViewer Scale

I am making a C# WPF application. I have a Canvas inside of a ScrollViewer that the user can add UIElements to. There is a button somewhere else on the screen that when clicked is supposed to Zoom in as much as possible so that the elements are still visible (see code). However, it loops indefinitely because the scale is not changing in the view. I added scrollViewer.UpdateLayout(); to ZoomOut() and ZoomIn(), but it still isn't updating. The zoom functions work as expected when using the scroll wheel or other on screen buttons. Any suggestions? Eventually it tries to zoom in so many times that UpdateLayout will break due to an error about the canvas' desired size.

    private void ZoomFit(object sender, RoutedEventArgs e)
    {
        UIElement leftMostComponent = null;
        UIElement rightMostComponent = null;
        UIElement topMostComponent = null;
        UIElement botMostComponent = null;
        //find the edgemost elements
        foreach (UIElement element in canvas.Children)
        {
            if (element is Components.IComponent component)
            {
                if (leftMostComponent != null)
                {
                    if (Canvas.GetLeft(element) < Canvas.GetLeft(leftMostComponent))
                    {
                        leftMostComponent = element;
                    }
                }
                else
                {
                    leftMostComponent = element;
                }
                if (rightMostComponent != null)
                {
                    if (Canvas.GetLeft(element) > Canvas.GetLeft(rightMostComponent))
                    {
                        rightMostComponent = element;
                    }
                }
                else
                {
                    rightMostComponent = element;
                }
                if (topMostComponent != null)
                {
                    if (Canvas.GetTop(element) < Canvas.GetTop(topMostComponent))
                    {
                        topMostComponent = element;
                    }
                }
                else
                {
                    topMostComponent = element;
                }
                if (botMostComponent != null)
                {
                    if (Canvas.GetTop(element) > Canvas.GetTop(botMostComponent))
                    {
                        botMostComponent = element;
                    }
                }
                else
                {
                    botMostComponent = element;
                }
            }
        }

        bool cont = true;
        if (AllInView(leftMostComponent, rightMostComponent, topMostComponent, botMostComponent))
        {
            int zoomCount = 0;
            while (cont)
            {
                //this.UpdateLayout();
                if (AllInView(leftMostComponent, rightMostComponent, topMostComponent, botMostComponent))
                {
                    ZoomIn();
                    zoomCount++;
                }
                else
                {
                    ZoomOut();
                    cont = false;
                }
            }
        }
        else
        {
            while (cont)
            {
                //this.UpdateLayout();
                if (AllInView(leftMostComponent, rightMostComponent, topMostComponent, botMostComponent))
                {
                    ZoomIn();
                    cont = false;
                }
                else
                {
                    ZoomOut();
                }
            }
        }

    private void ZoomIn()
    {
        scaleTransform.ScaleX *= 1.1;
        scaleTransform.ScaleY *= 1.1;
        scrollViewer.UpdateLayout();
        this.UpdateLayout();
    }

    private bool AllInView(UIElement leftMostElement, UIElement rightMostElement, UIElement topMostElement, UIElement botMostElement)
    {
        return IsObjectInView(canvas, leftMostElement) && IsObjectInView(canvas, rightMostElement) && IsObjectInView(canvas, botMostElement) && IsObjectInView(canvas, topMostElement);
    }

    private static bool IsObjectInView(Canvas canvas, UIElement element)
    {
        // Get the bounding rectangle of the element
        Rect elementBounds = element.TransformToAncestor(canvas).TransformBounds(new Rect(element.RenderSize));

        // Get the viewport rectangle (visible area of the canvas)
        Rect viewportBounds = new Rect(new Point(0, 0), canvas.RenderSize);

        // Check if the element's bounds intersect with the viewport's bounds
        return viewportBounds.IntersectsWith(elementBounds);
    }

Upvotes: -1

Views: 56

Answers (1)

Ermanator
Ermanator

Reputation: 17

The problem was I needed to check if it was in view of the ScrollViewer not the canvas.

After doing that it was cutting off the edgemost elements, so I reconfigured AllInView like so

    private bool AllInView(UIElement leftMostElement, UIElement rightMostElement, UIElement topMostElement, UIElement botMostElement)
    {
        // Get the viewport rectangle (visible area of the canvas)
        Rect viewportBounds = new Rect(new Point(0, 0), scrollViewer.RenderSize);
        Rect leftMostElementBounds = leftMostElement.TransformToAncestor(scrollViewer).TransformBounds(new Rect(leftMostElement.RenderSize));
        Rect rightMostElementBounds = rightMostElement.TransformToAncestor(scrollViewer).TransformBounds(new Rect(rightMostElement.RenderSize));
        Rect topMostElementBounds = topMostElement.TransformToAncestor(scrollViewer).TransformBounds(new Rect(topMostElement.RenderSize));
        Rect botMostElementBounds = botMostElement.TransformToAncestor(scrollViewer).TransformBounds(new Rect(botMostElement.RenderSize));
        return viewportBounds.Contains(leftMostElementBounds.TopLeft) && viewportBounds.Contains(rightMostElementBounds.TopRight) && viewportBounds.Contains(topMostElementBounds.TopRight) && viewportBounds.Contains(botMostElementBounds.BottomRight);
    }

Upvotes: -1

Related Questions