16

I have learned that if the height of a grid row, where the ScrollViewer resides, is set as Auto, the vertical scroll bar will not take effect since the actual size of the ScrollViewer can be larger than the height in sight. So in order to make the scroll bar work, I should set the height to either a fixed number or star height

However, I now have this requirement, that I have two different views reside in two grid rows, and I have a toggle button to switch between these two views: when one view is shown, the other one is hidden/disappeared. So I have defined two rows, both heights are set as Auto. And I bind the visibility of the view in each row to a boolean property from my ViewModel (one is converted from True to Visible and the other from True to Collapsed. The idea is when one view's visibility is Collapsed, the height of the grid row/view will be changed to 0 automatically.

The view show/hidden is working fine. However, in one view I have a ScrollViewer, which as I mentioned doesn't work when the row height is set as Auto. Can anybody tell me how I can fulfill such requirement while still having the ScrollViewer working automatically`? I guess I can set the height in code-behind. But since I am using MVVM, it would require extra communication/notification. Is there a more straightforward way to do that?

4

5 回答 5

30

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

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

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

但是如果父控件的高度是无限的呢?

如果父控件的高度是无限的,那么我们就有更大的问题了。我们必须不断设置所有父母的高度,直到我们碰到一个非无限高度的控件。

Snoop在这方面绝对是无价的:

在此处输入图像描述

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

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

提示:VerticalAlignment="Stretch"如果您是Grid带有 a的 a 的孩子,则使用,如果这不起作用<RowDefinition Height="*">,则使用其他地方。Binding RelativeSource...


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

附录 A:之前的尝试 1

也可以这样使用:

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

附录 B:之前的尝试 2

有用信息:请参阅Auto Height 与 MaxHeight 的组合

如果似乎没有任何效果,可能是因为ActualHeight父级的 0 (所以什么都不可见)或很大(所以滚动查看器永远不需要出现)。如果有深度嵌套的网格,并且底部有一个滚动查看器,这将是一个更大的问题。

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

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

  • Height将的StackPanel或绑定GridActualHeight父级的 。

最后,ScrollViewer在 this 里面放一个StackPanel

附录 C:之前的尝试 3

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

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

原因?如果绑定失败,高度将为零,并且什么也看不到。如果我们绑定到不可访问的元素,绑定可能会失败。如果我们up从可视化树down到叶节点(例如,向上到父网格,然后向下到ActualHeight附加到该网格的行),绑定将失败。这就是为什么绑定到ActualWidthaRowDefinition根本行不通的原因。

附录 D:先前的尝试 4

我最终通过确保从我们到UserControl 中Height=Auto的第一个元素的所有父元素来完成这项工作。<Grid>

于 2014-09-04T10:43:20.683 回答
16

如果可以的话,将高度从 更改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>
于 2013-10-14T08:43:03.090 回答
0

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

最后,如果您都不喜欢两者,只需按照 swiszcz 的建议将行的高度设置为 * 或破解 wpf 编写您自己的自定义面板,该面板将能够在每个平行宇宙或类似的东西中布置所有可能的东西。:)

于 2013-10-14T08:46:44.130 回答
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>

于 2020-05-03T20:35:22.613 回答
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> 

我将它动态添加ContentControlPage.

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

享受!

PS 其实我是为 UWP 做的。

于 2019-02-05T02:46:42.303 回答