简体   繁体   中英

ScrollViewer doesn't scroll for custom panel

I am making my own custom panel, which is supposed to scroll vertically when the content does not fit the available space, so i put it in a ScrollViewer. Right now i can't get the ScrollViewer to activate the scrollbar when the panel inside is bigger then the ScrollViewer itself.

The permille functions get attached properties telling how big the childs have to be compared to the available size (without scrolling), aka the ViewPort. As the size passed in MeasureOverride passes infinite, i don't think i can use the permille functions there. That is why i measure my children in ArrangeOverride (not best practice, i guess) but that way the scrollviewer doesn't scroll.

How do i get this to work?

My XAML code:

<ScrollViewer>
    <controls:TilePanel x:Name="TilePanel" PreviewMouseLeftButtonDown="TilePanel_PreviewMouseLeftButtonDown" PreviewMouseLeftButtonUp="TilePanel_PreviewMouseLeftButtonUp" 
                        PreviewMouseMove="TilePanel_PreviewMouseMove" DragEnter="TilePanel_DragEnter" Drop="TilePanel_Drop" AllowDrop="True" />
</ScrollViewer>

My Custom Panel Class:

/// <summary>
/// A Panel Showing Tiles
/// </summary>
public class TilePanel : PermillePanel
{
        public TilePanel()
        {
        }

        protected override Size MeasureOverride(Size constraint)
        {
            //here constraint width or height can be infinite.
            //as tiles are a permille of that height, they too can be infinite after measuring
            //this is unwanted behavior, so we measure in the ArrangeOverride method

            if (constraint.Width == double.PositiveInfinity)
            {
                return new Size(0, constraint.Height);
            }
            else if (constraint.Height == double.PositiveInfinity)
            {
                return new Size(constraint.Width, 0);
            }
            else
            {
                return constraint;
            }
        }

        protected override Size ArrangeOverride(Size arrangeSize)
        {
            //return base.ArrangeOverride(arrangeSize);

            foreach (FrameworkElement child in InternalChildren)
            {
                Size availableSize = new Size();
                //set the width and height for the child
                availableSize.Width = arrangeSize.Width * TilePanel.GetHorizontalPermille(child) / 1000;
                availableSize.Height = arrangeSize.Height * TilePanel.GetVerticalPermille(child) / 1000;

                child.Measure(availableSize);

            }

            // arrange the children on the panel
            // fill lines horizontally, when we reach the end of the current line, continue to the next line

            Size newSize = new Size(arrangeSize.Width, arrangeSize.Height);

            double xlocation = 0;
            double ylocation = 0;

            double ystep = 0;

            double maxYvalue = 0;

            foreach (FrameworkElement child in InternalChildren)
            {
                double endxlocation = xlocation + child.DesiredSize.Width;

                double constrainedWidth = arrangeSize.Width * TilePanel.GetHorizontalPermille(child) / 1000;
                double constrainedHeight = arrangeSize.Height * TilePanel.GetVerticalPermille(child) / 1000;

                if (TilePanel.GetVerticalPermille(child) != 0 && TilePanel.GetHorizontalPermille(child) != 0)
                {
                    //horizontal overflow -> next line
                    if (endxlocation >= this.DesiredSize.Width *1.01)
                    {
                        ylocation += ystep;
                        xlocation = 0;
                    }
                }

                Rect rect = new Rect(xlocation, ylocation, constrainedWidth, constrainedHeight);
                child.Arrange(rect);
                xlocation += constrainedWidth;
                ystep = Math.Max(ystep, constrainedHeight);
                maxYvalue = Math.Max(maxYvalue, ystep + constrainedHeight);
            }

            if (maxYvalue > newSize.Height)
            {
                newSize.Height = maxYvalue;
            }

            return newSize;
        }
    }

Calling Measure() from within ArrangeOverride() will cause problems. The framework detects this and forces a remeasure. Set a tracepoint in MeasureOverride() , and I'll bet you'll see that it keeps getting called over and over again, even though the layout hasn't changed 1 .

If you absolutely have to call Measure() from ArrangeOverride() , you will need to do so conditionally such that it only forces a remeasure when the available size actually changes since the last call to Measure() . Then, you'll effectively end up with two measure + arrange passes any time the layout is invalidated, as opposed to just one. However, such an approach is hacky, and I would advise sticking to the best practice of only measuring within MeasureOverride() .


1 Interestingly, your UI may still respond to input, despite this apparent "infinite loop" in the layout.

If you want to use a custom Panel inside a ScrollViewer then you must add the code that does the actual scrolling. You can do that by implementing the IScrollInfo Interface in your custom Panel .

You can find a tutorial that explains this interface and provides an example code imeplementation in the WPF Tutorial - Implementing IScrollInfo page on the Tech Pro website. It's a fairly simple procedure and looks a tiny bit like this:

public void LineDown() { SetVerticalOffset(VerticalOffset + LineSize); }

public void LineUp() { SetVerticalOffset(VerticalOffset - LineSize); }

public void MouseWheelDown() { SetVerticalOffset(VerticalOffset + WheelSize); }

public void MouseWheelUp() { SetVerticalOffset(VerticalOffset - WheelSize); }

public void PageDown() { SetVerticalOffset(VerticalOffset + ViewportHeight); }

public void PageUp() { SetVerticalOffset(VerticalOffset - ViewportHeight); }

...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM