如何在WPF中将高度设置为自动时使scrollviewer工作?

时间:2013-10-14 08:02:57

标签: wpf xaml uwp-xaml scrollviewer autosize

我了解到,如果ScrollViewer所在的网格行的高度设置为Auto,则垂直滚动条将不会生效,因为{{1}的实际大小可以大于视线中的高度。因此,为了使滚动条工作,我应该将高度设置为固定数字或星形高度

但是,我现在有了这个要求,我有两个不同的视图驻留在两个网格行中,并且我有一个切换按钮可以在这两个视图之间切换:当显示一个视图时,另一个视图隐藏/消失。所以我定义了两行,两个高度都设置为ScrollViewer。我将每行中视图的可见性绑定到我的ViewModel的布尔属性(一个从Auto转换为True,另一个从Visible转换为True。这个想法是当一个视图的可见性为Collapsed时,网格行/视图的高度将自动更改为0.

视图显示/隐藏工作正常。但是,在一个视图中,我有Collapsed,正如我所提到的,当行高设置为ScrollViewer时,它不起作用。任何人都能告诉我如何在Auto自动工作的同时满足这样的要求吗?我想我可以在代码隐藏中设置高度。但由于我使用的是MVVM,因此需要额外的通信/通知。有没有更简单的方法呢?

5 个答案:

答案 0 :(得分:21)

在MVVM中,对我有用的方法是将ScrollViewer的高度绑定到父控件的ActualHeight(始终为UIElement类型)。

ActualHeight是一个只读属性,仅在控件绘制到屏幕上后才设置。如果调整窗口大小,它可能会改变。

<StackPanel>
    <ScrollViewer Height="{Binding Path=ActualHeight, 
           RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}">
        <TextBlock Text=Hello"/>
    </ScrollViewer>
</StackPanel>

但如果父控件有无限高度怎么办?

如果父控件具有无限高度,那么我们就会遇到更大的问题。我们必须继续设置所有父母的身高,直到我们击中一个非无限高度的控件。

Snoop绝对是非常宝贵的:

enter image description here

如果任何XAML元素的“高度”为0NaN,您可以使用以下方法之一将其设置为:

  • Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}"
  • VerticalAlignment="Stretch"
  • Height="Auto"

提示:如果您是VerticalAlignment="Stretch" Grid的孩子,请使用<RowDefinition Height="*">,如果不起作用则使用Binding RelativeSource...


如果您有兴趣,以下是我之前解决此问题的所有尝试:

附录A:以前的尝试1

也可以使用:

Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}"

附录B:以前的尝试2

有用信息:请参阅WPF - Auto Height in combination with MaxHeight

如果似乎没有任何效果,可能是因为父项的ActualHeight为0(因此没有任何内容可见)或巨大(因此滚动查看器永远不需要出现)。如果存在深度嵌套的网格,并且滚动查看器位于底部,那么这就更成问题了。

  • 使用Snoop查找父ActualHeight的{​​{1}}。在属性中,按照StackPanel字词进行过滤,这会带回"Actual"ActualHeight
  • 如果ActualWidth为零,请使用ActualHeight给它一个最小高度,这样我们至少可以看到一些东西。
  • 如果MinHeight太大以至于它离屏幕边缘(即16,000),请使用ActualHeight给它一个合理的最大高度,这样就会出现滚动条。

滚动条出现后,我们可以进一步清理它:

  • MaxHeightHeight的{​​{1}}绑定到父级的StackPanel

最后,将Grid放入此ActualHeight

附录C:以前的尝试3

事实证明,这有时会失败:

ScrollViewer

原因?绑定失败,高度为零,没有任何东西可见。如果我们绑定到不可访问的元素,绑定可能会失败。如果我们将StackPanel视觉树,然后Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}" 传递给叶节点(例如,直到父网格,然后直到连接到该网格的行的up,则绑定将失败格)。这就是为什么绑定到down的{​​{1}}根本不起作用。

附录D:以前的尝试4

我最终通过确保ActualHeight来自我们的所有父元素到UserControl中的第一个ActualWidth元素来完成此工作。

答案 1 :(得分:13)

如果可以,请将高度从Auto更改为*

示例:

    <Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="525">
    <StackPanel Orientation="Horizontal"  Background="LightGray">

        <Grid Width="100">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="_scroll1">
                <Border Height="300" Background="Red" />
            </ScrollViewer>
            <TextBlock Text="{Binding ElementName=_scroll1, Path=ActualHeight}" Grid.Row="1"/>
        </Grid>

        <Grid Width="100">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
                <ScrollViewer VerticalScrollBarVisibility="Auto" x:Name="_scroll2">
                    <Border Height="300" Background="Green" />
                </ScrollViewer>
            <TextBlock Text="{Binding ElementName=_scroll2, Path=ActualHeight}" Grid.Row="1"/>
        </Grid>
    </StackPanel>
</Window>

答案 2 :(得分:0)

您可以在ScrollViewer上设置修复高度,但是您必须考虑网格的第二行也具有该高度,因为行的第一个子项将是ScrollViewer,行的高度是auto,或者您绑定高度ScrollViewer到您布局中的另一个控件。我们不知道您的布局看起来如何。

最后,如果你不喜欢两者都不喜欢将行的高度设置为*,如swiszcz建议的那样,或者黑客wpf编写你自己的自定义面板,它将能够在每个并行的宇宙中布置所有可能的东西或类似的东西。 :)

答案 3 :(得分:0)

我发现,您必须将ScrollViewer放在有Height=Auto的容器中,或者得到他的父母Heigh Actual Size并将其应用于该容器。

就我而言,我有UserControl喜欢

  <Grid Margin="0,0,0,0" Padding="0,2,0,0">
                    <ScrollViewer Height="Auto" ZoomMode="Disabled" IsVerticalScrollChainingEnabled="True"  VerticalAlignment="Top"
                     HorizontalScrollMode="Enabled" HorizontalScrollBarVisibility="Disabled"
                     VerticalScrollMode="Enabled" VerticalScrollBarVisibility="Visible"> 
                        <ListView  ItemsSource="{x:Bind PersonalDB.View, Mode=OneWay}" x:Name="DeviceList"
                           ScrollViewer.VerticalScrollBarVisibility="Hidden" 
                    ItemTemplate="{StaticResource ContactListViewTemplate}"
                    SelectionMode="Single"
                    ShowsScrollingPlaceholders="False"
                    Grid.Row="1" 
                    Grid.ColumnSpan="2"
                    VerticalAlignment="Stretch"
                    BorderThickness="0,0,0,0"
                    BorderBrush="DimGray">
                            <ListView.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <ItemsStackPanel AreStickyGroupHeadersEnabled="False" />
                                </ItemsPanelTemplate>
                            </ListView.ItemsPanel>
                            <ListView.GroupStyle>
                                <GroupStyle>
                                    <GroupStyle.HeaderTemplate>
                                        <DataTemplate x:DataType="local1:GroupInfoList">
                                            <TextBlock Text="{x:Bind Key}" 
                                       Style="{ThemeResource TitleTextBlockStyle}"/>
                                        </DataTemplate>
                                    </GroupStyle.HeaderTemplate>
                                </GroupStyle>
                            </ListView.GroupStyle>
                        </ListView>
                    </ScrollViewer>
                </Grid> 

然后我将它动态地添加到ContentControl内的Page中。

 <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="0,0,12,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="70"  /> 
            <RowDefinition Height="*" MinHeight="200"  />
        </Grid.RowDefinitions> 
 <Grid Grid.Row="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"     >
            <ContentControl x:Name="UIControlContainer"  />
        </Grid>
    </Grid>

请注意,Heigh中的Row*

当我填充ContentControl时,我会在Loaded事件中使用此代码

  UIControlContainer.Content = new UIDeviceSelection() { 
VerticalAlignment = VerticalAlignment.Stretch,
HorizontalAlignment = HorizontalAlignment.Stretch,
Height = UIControlContainer.ActualHeight,
Width = UIControlContainer.ActualWidth
};

此外,当ContentControl更改大小时,您还必须更新UserControl的大小。

 UIControlContainer.SizeChanged += UIControlContainer_SizeChanged;

 private void UIControlContainer_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (UIControlContainer.Content != null)
            {
                if (UIControlContainer.Content is UserControl)
                {
                    (UIControlContainer.Content as UserControl).Height = UIControlContainer.ActualHeight;
                    (UIControlContainer.Content as UserControl).Width = UIControlContainer.ActualWidth;
                }
            }
        }

享受!

P.S。大概我是为UWP做的。

答案 4 :(得分:0)

我遇到了类似的问题,花了我几个小时才能找出解决方案。解决问题的方法是使用Dockpanel作为父容器,而不是StackPanel。如果功能应该类似于垂直堆栈面板,则只需指定所有子项停靠在顶部即可。考虑在Dock XAML中使用LastChildFill =“ False”(这不是默认设置)。

所以代替:

<StackPanel Orientation="Horizontal">
  <Textbox>SomeTextBox</Textbox>
  <Scrollviewer/>
</StackPanel>
尝试:
<DockPanel LastChildFill="False">
  <Textbox DockPanel.Dock="Top">SomeTextBox</Textbox>
  <Scrollviewer DockPanel.Dock="Top"/>
</DockPanel>