如何在 StackPanel 中使用 ScrollViewer?

79

在以下的WPF XAML中,ScrollViewer无法正常工作(它显示了一个滚动条,但您无法滚动,而内容会超出窗口底部)。

我可以将外部的StackPanel更改为Grid并使其工作。

然而,在我从中复制以下代码的应用程序中,我需要有一个外部的StackPanel。我该如何修改StackPanel才能使ScrollViewer显示可用的滚动条?例如VerticalAlignment="Stretch"和Height="Auto"都不起作用。

 <StackPanel>
        <ScrollViewer>
            <StackPanel>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
            </StackPanel>
        </ScrollViewer>
 </StackPanel>
9个回答

67

这个问题也一度困扰了我,解决方法是将你的StackPanel嵌套在一个ScrollViewer中。

此外,你需要确保将滚动视图器(scroll viewer)的CanContentScroll属性设置为True,以下是一个示例:

  <ScrollViewer Grid.Row="1" Margin="299,12,34,54" Name="ScrollViewer1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Height="195" CanContentScroll="True">
        <StackPanel Name="StackPanel1" OverridesDefaultStyle="False"  Height="193" Width="376" VerticalAlignment="Top" HorizontalAlignment="Left"></StackPanel>
  </ScrollViewer>

CanContentScroll属性在哪里?请参见http://msdn.microsoft.com/en-us/library/system.windows.controls.scrollviewer_properties(v=VS.95).aspx - gideon
3
请查看此链接:http://msdn.microsoft.com/zh-cn/library/ms612683.aspx - Kushal Waikar
3
请确保将滚动视图器(ScrollViewer)的CanContentScroll属性设置为True。我仍然无法相信,一个名为“ScrollViewer”的控件默认情况下居然不是这样设置的。 - Andrea Antonangeli
1
@AndreaAntonangeli 我认为 'CanContentScroll' 不是你想象的意思。当值为 'true' 时,滚动是按项(或内容片段)进行的;当值为 'false' 时,仍会发生滚动,但以像素为单位。 - mcalex
为什么要设置 CanContentScroll - Lyndon Gingerich
顺便提一下,这里有一个更具体的文档链接:https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.scrollviewer.cancontentscroll - Lyndon Gingerich

64

如果不指定 StackPanel 的高度,它就无法实现你想要的效果。因为它被设计成可以在一个方向上无限增长。建议使用其他类型的 Panel。那么为什么你需要外层的 StackPanel 呢?


15
我想把东西堆叠起来,使用网格需要手动管理所有的行和列,但DockPanel很好用,所以我会转用它,谢谢。 - Edward Tanguay
1
我同意Edward的观点。根据我的经验,将我的DataGrid包裹在一个DockPanel中,然后为每个DataGrid设置DockPanel.Dock="Top"效果很好。 - BitsAndBytes
2
我应该在UWP中使用哪种替代控件?没有DockPanel。谢谢。 - Jan Chalupa
1
对于 UWP,您可以使用 RelativePanel。 - xmashallax
2
该死的StackPanel,在UWP中我总是不得不用Grid来替换它,他们应该改变它的行为,因为这是唯一一个以这种方式工作的面板。 - Alberto Rivelli
固定内部 StackPanel 的高度就可以了,因为这样 ScrollViewer 就会从其子元素中获取高度,但是如果内部 StackPanel 的高度增加,ScrollViewer 也会相应增加。简而言之,在 ScrollViewer 的高度为 NaN 时,它将根据其内容大小进行自适应。另一方面,如果设置了 ScrollViewer 的高度,则它不会扩展到其内容。因此,这很可能是更好的选择。 - Kylo Ren

10
请注意,有时您可能会使用StackPanel而不自知。在我的情况下,我有以下代码:
<ScrollViewer>
  <ItemsControl ItemsSource="{Binding Pages}"/>
</ScrollViewer>

这个之前运行得很好。绑定引用的“页面”实际上是不同的,复杂的用户控件,我只想在其中一些上面显示滚动条。因此,我移除了滚动视图器:

 <ItemsControl ItemsSource="{Binding Pages}"/>

然后我将ScrollViewer放置在我想要的用户控件的顶部元素上。然而,这并没有起作用。内容仅仅从页面上流出来了。起初我认为这个问题/答案对我没有帮助,但是后来我意识到ItemsControl的默认ItemPanel是StackPanel。所以我通过指定一个不是StackPanel的ItemsPanel来解决了我的问题:

<ItemsControl ItemsSource="{Binding Pages}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

5
这是它的工作原理:
<Window x:Class="TabControl.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
    xmlns:local="clr-namespace:TabControl"
    Title="MainWindow"    Height="300"   
    DataContext="{Binding RelativeSource={RelativeSource Self}}"         
    >    
<StackPanel>
    <ScrollViewer Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Border}},Path=ActualHeight}" >
        <StackPanel >
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
            <TextBlock Text="This is a test"/>                <TextBlock Text="This is a test"/>
        </StackPanel>
    </ScrollViewer>
</StackPanel>

通过将ScrollViewer的高度绑定到窗口的内部高度。

重新调整大小的逻辑是,我们需要给任何元素固定的高度或设计视图以使用渲染高度。

输出:

Stackpanel中的滚动条


@AlanMcBee 是的,可能会有许多情况下它不能完美地工作,但这是控制层次结构的最基本情况,我已经给出了解决方案。但考虑到逻辑,在大多数情况下,您只需要更改绑定中的祖先类型,它就应该再次完美地工作。修复的关键在于层次结构中有一个UI元素可以帮助我们依赖高度(不一定是边框),只要您能找到可靠的高度,逻辑就可以保持不变。希望这有意义,否则请将您的问题发布为问题,我会尽力帮助。 :) - Kylo Ren
找不到 x:Type - Bigeyes
@Bigeyes,你使用的是哪个.NET版本和VS版本? - Kylo Ren
@KyloRen。Visual Studio 2010和.Net 4.0。 - Bigeyes
@Bigeyes x:Type 是 WPF 的本地语法,你的设计师或编译器可能存在问题。 - Kylo Ren
显示剩余2条评论

5

实际上,我解决这个问题的方法是移除外部堆栈面板,而是在主网格内设置所需位置的滚动视图器。

        <Grid Style="{StaticResource LayoutRootStyle}">
    <Grid.RowDefinitions>
        <RowDefinition Height="160"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>        

    <!-- Vertical scrolling grid used in most view states -->    

        <ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto">
            <StackPanel Orientation="Horizontal">
                <GridView>
                ...
                </GridView>
            </StackPanel>
        </ScrollViewer>        

将ScrollView放在高度为“*”的行中的网格中,这对我解决了问题。 - Mc_Topaz

2
如果您的stack panel在网格中,我会这样做:

以下是我的实现方法:

<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
    <StackPanel MaxHeight="{Binding Path=Height,RelativeSource={RelativeSource 
              AncestorType=Grid}}">
    </StackPanel>
</ScrollViewer>

1

将Grid.Row="1"从StackPanel移至ScrollViewer,完全解决了我的问题。

我有一个很长的列表,其中包含40个项目要在StackPanel中显示,但只显示了前20个。

    <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
        <StackPanel x:Name="ContentPanel" Margin="12,0,12,0">
        <TextBlock Text="{Binding Line1}" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
        <TextBlock Text="" Margin="10,-2,10,0" Style="{StaticResource PhoneTextNormalStyle}" />
        ...
        </StackPanel>
    </ScrollViewer>

0
<WrapPanel Orientation="Vertical">
        <ScrollViewer>
            <WrapPanel Orientation="Vertical">
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
                <TextBlock Text="This is a test"/>
            </WrapPanel>
        </ScrollViewer>
    </WrapPanel>

1
目前你的回答不够清晰。请编辑并添加更多细节,以帮助其他人理解它如何回答所提出的问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community

0

我在WinUI 3、.Net6中遇到了完全相同的问题(其中StackPanel仍无法原生容纳ScrollViewer子项)

这个修复方法也应该适用于WPF,并允许您将ScrollViewer的父级保持为StackPanel:

  1. 找到一个 xaml 窗口/页面上具有以下特征的元素*:
  • 它具有您想要的ScrollViewer高度。
  • 它具有固定高度或者是具有其分配的Grid RowDefinition Height="*"
  • 它是您的ScrollViewer之外的元素。
  • 它可以是您的ScrollViewer的祖先或不是。
  • 它不是StackPanel
  1. 确保您找到的元素*设置了名称:
  • 例如x:Name="myControlName"
  1. 将您的ScrollViewer的“高度”设置为该元素*的“高度”或“ActualHeight”的继承值:
  • <ScrollViewer Height="{Binding ActualHeight, ElementName=myControlName}"/>
  1. 收益!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接