WPF包装面板和滚动

时间:2010-01-22 17:08:01

标签: c# wpf scroll wrappanel

我有一个简单的WrapPanel,其中包含许多宽控件。当我调整Width的{​​{1}}时,一切都按预期工作。如果有足够的空间,控件将在一行上进行,或者当没有空格时,控件将包含在下一行。

然而,我需要发生的是,如果所有控件基本上都是垂直堆叠的(因为没有更多的水平空间),Window的{​​{1}}会更多地减少,出现水平滚动条,以便我可以滚动并查看整个控件。下面是我的xaml。我尝试将Width包裹在Window但我无法实现目标。

WrapPanel

因此,如果您将上述ScrollViewer的{​​{1}}缩小到最小值,您将无法看到按钮的文字。我希望出现一个水平滚动条,以便我可以滚动查看文本,但不会干扰通常的包装功能。

感谢。

更新 我已经按照下面的Paul的建议,水平滚动条现在按预期显示。但是,我也想要垂直滚动,所以我设置了<Window x:Class="WpfQuotes.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="Auto" Width="600" Foreground="White"> <WrapPanel> <Button Width="250">1</Button> <Button Width="250">2</Button> <Button Width="250">3</Button> </WrapPanel> </Window> 。问题是,如果我调整窗口大小以便显示垂直滚动条,即使不需要(也有足够的水平空间来查看整个控件),也会始终显示水平滚动条。好像出现的垂直滚动条会弄乱滚动查看器的宽度。有没有办法纠正这个问题,除非实际需要水平滚动条,否则不会出现?

下面是我的xaml和我在Width中添加的唯一代码:

Window

VerticalScrollBarVisibility="Auto"中唯一被覆盖的内容:

CustomWrapPanel

3 个答案:

答案 0 :(得分:47)

这就是,如果你要使用一个包装面板,它会做两件事,它会在一个方向上占用尽可能多的可用空间,并在另一个方向上根据需要进行扩展。例如,如果你将它放在一个窗口内,就像你拥有它一样,它会占用尽可能多的水平空间,然后根据需要向下扩展,这就是垂直滚动条可以工作的原因,父容器说“这是我有多宽,但是你可以让自己变得像你想要的一样大“,如果你把它改成水平滚动条,滚动查看器基本上是说”这是你有多高,但你可以像你想要“在这种情况下,包装面板不会换行,因为没有水平约束。

一个可能的解决方案是改变包裹面板从水平到垂直的方向(这可能不是理想或预期的行为):

    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
        <WrapPanel Orientation="Vertical">
            <Button Width="250">1</Button>
            <Button Width="250">2</Button>
            <Button Width="250">3</Button>
        </WrapPanel>
    </ScrollViewer>

为了获得您期望的行为,您必须做更接近这一点的事情:

    <ScrollViewer x:Name="MyScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
        <WrapPanel MinWidth="250" Width="{Binding ElementName=MyScrollViewer, Path=ViewportWidth}">
            <Button Width="250">1</Button>
            <Button Width="250">2</Button>
            <Button Width="250">3</Button>
        </WrapPanel>
    </ScrollViewer>

然而,第二个解决方案只有在您已经知道子元素的宽度时才有效,理想情况下您希望将最大宽度设置为最大子项的实际宽度,但为了做到这一点,您需要创建一个从包装面板派生的自定义控件,并自己编写代码以检查它。

答案 1 :(得分:1)

这是我的解决方案:

    <Grid Width="475">
        <ItemsControl ItemsSource="{Binding Items}" 
                          Height="450" Width="475" >

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:HorizontalListItemControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>

            <ItemsControl.Template>
                <ControlTemplate>
                    <ScrollViewer>
                        <ItemsPresenter />
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>

        </ItemsControl>
    </Grid>



我将尝试解释:
我使用了ItemsControl,它的ItemsSource绑定到我的Items集合。 在其中,我定义了一个WrapPanel作为ItemsPanelTemplate。这就是使包装工作完成的原因。

            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>


但是现在没有滚动了,对吗?
为了解决这个问题,我在ScrollViewer中定义了ItemsPresenter作为ControlTemplate:

            <ItemsControl.Template>
                <ControlTemplate>
                    <ScrollViewer>
                        <ItemsPresenter />
                    </ScrollViewer>
                </ControlTemplate>
            </ItemsControl.Template>


现在您可以滚动了。



希望我能帮上忙。

答案 2 :(得分:0)

     public bool CheckUIElementInBounary(UIElement element, Rect r)
            {
                bool inbound = false;
                Point p1 = element.PointToScreen(new Point(0, 0));
                Point p2 = element.PointToScreen(new Point(0, element.RenderSize.Height));
                Point p3 = element.PointToScreen(new Point(element.RenderSize.Width, 0));
                Point p4 = element.PointToScreen(new Point(element.RenderSize.Width, element.RenderSize.Height));
                if (CheckPoint(p1, r) || CheckPoint(p2, r) || CheckPoint(p3, r) || CheckPoint(p4, r))
                {
                    inbound = true;
                }
                return inbound;
            }
            public bool CheckPoint(Point p, Rect bounday)
            {
                bool inbound = false;
                if (p.X >= bounday.Left && p.X <= bounday.Right && p.Y <= bounday.Top && p.Y <= bounday.Bottom)
                {
                    inbound = true;
                }
                return inbound;
            }

===================
void mainViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            foreach (var item in this.mainContent.Items)
            {
                Button btn = item as Button;
                Point p1 = mainViewer.PointToScreen(new Point(0, 0));
                Point p2 = mainViewer.PointToScreen(new Point(mainViewer.ActualWidth, mainViewer.ActualHeight));
                Rect bounds = new Rect(p1, p2);
                if (!CheckUIElementInBounary(btn, bounds))
                {
                    this.Title = btn.Content.ToString();
                    mainContent.ScrollIntoView(btn);
                    break;
                }
            }

        }