ScrollViewer不会滚动自定义面板

时间:2014-09-23 11:45:32

标签: c# wpf scrollviewer

我正在创建自己的自定义面板,当内容不适合可用空间时,它应该垂直滚动,所以我把它放在ScrollViewer中。 现在,当内部面板比ScrollViewer本身更大时,我无法让ScrollViewer激活滚动条。

permille函数获得附加属性,告诉孩子必须与可用大小(不滚动)(即ViewPort)进行比较。 由于MeasureOverride中传递的大小无限,我认为我不能在那里使用permille函数。 这就是为什么我在ArrangeOverride中测量我的孩子(不是最佳实践,我猜)但滚动查看器不会滚动。

我如何让这个工作?

我的XAML代码:

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

我的自定义面板类:

/// <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;
        }
    }

2 个答案:

答案 0 :(得分:1)

Measure()内拨打ArrangeOverride()会导致问题。框架检测到这一点并强制重新测量。在MeasureOverride()中设置一个跟踪点,我打赌你会发现它一直被反复调用,即使布局没有改变 1

如果您必须从Measure()拨打ArrangeOverride(),则需要有条件地执行此操作,以便仅在自上次呼叫{{1}后可用大小实际发生更改时强制重新测量}。然后,只要布局无效,您就会有效地结束两个度量+排列传递。但是,这种方法很容易,我建议坚持只在Measure()内进行测量的最佳做法。


1 有趣的是,尽管存在明显的“无限循环”,您的UI仍可能会对输入做出响应。在布局中。

答案 1 :(得分:0)

如果要在Panel内使用自定义ScrollViewer,则必须添加执行实际滚动的代码。您可以通过在自定义Panel中实施IScrollInfo Interface来实现这一目标。

您可以在Tech Pro网站的WPF Tutorial - Implementing IScrollInfo页面找到解释此界面的教程,并提供示例代码补充。这是一个相当简单的过程,看起来有点像这样:

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); }

...