如何使滚动查看器在 WPF 中将高度设置为自动?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19355818/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:58:36  来源:igfitidea点击:

How to make scrollviewer work with Height set to Auto in WPF?

wpfxamluwp-xamlscrollviewerautosize

提问by tete

I have learned that if the height of a grid row, where the ScrollViewerresides, is set as Auto, the vertical scroll bar will not take effect since the actual size of the ScrollViewercan 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

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

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 Trueto Visibleand the other from Trueto Collapsed. The idea is when one view's visibility is Collapsed, the height of the grid row/view will be changed to 0 automatically.

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

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 ScrollViewerworking 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?

视图显示/隐藏工作正常。但是,在一个视图中,我有一个ScrollViewer,正如我所提到的,当行高设置为Auto. 谁能告诉我如何在仍然ScrollViewer自动工作的同时满足这样的要求?我想我可以在代码隐藏中设置高度。但由于我使用的是 MVVM,因此需要额外的通信/通知。有没有更直接的方法来做到这一点?

采纳答案by Maciek ?wiszczowski

Change Height from Autoto *, if you can.

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

Example:

例子:

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

回答by Contango

In MVVM, the way that worked for me was to bind the height of the ScrollViewerto the ActualHeightof the parent control (which is always of type UIElement).

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

ActualHeightis a read-only property which is only set after the control has been drawn onto the screen. It may change if the window is resized.

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

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

But what if the parent control has an infinite height?

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

If the parent control has an infinite height, then we have a bigger problem. We have to keep setting the height of all parents, until we hit a control with a non-infinite height.

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

Snoop is absolutely invaluable for this:

Snoop 在这方面绝对是无价之宝:

enter image description here

在此处输入图片说明

If the "Height" for any XAML element is 0or NaN, you can set it to something using one of:

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

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

Hint: Use VerticalAlignment="Stretch"if you are a child of a Gridwith a <RowDefinition Height="*">, and the Binding RelativeSource...elsewhere if that doesn't work.

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



If you're interested, here is all of my previous attempts to fix this issue:

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

Appendix A: Previous Attempt 1

附录 A:之前的尝试 1

Can also use this:

也可以用这个:

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

Appendix B: Previous Attempt 2

附录 B:之前的尝试 2

Useful info: see WPF - Auto Height in combination with MaxHeight.

有用信息:请参阅WPF - Auto Height 与 MaxHeight 的结合

If nothing seems to work, it's probably because the ActualHeightof the parent is either 0 (so nothing is visible) or huge (so the scrollviewer never needs to appear). This is more of a problem if there are deeply nested grids, with a scrollviewer right at the bottom.

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

  • Use Snoop to find the ActualHeightof the parent StackPanel. In properties, filter by the word "Actual", which brings back ActualHeightand ActualWidth.
  • If ActualHeightis zero, give it a minimum height using MinHeight, so we can at least see something.
  • If ActualHeightis so huge that it goes off the edge of the screen (i.e. 16,000), give it a reasonable maximum height using MaxHeight, so the scrollbars will appear.
  • 使用 Snoop 查找ActualHeight父对象的StackPanel。在属性中,按单词过滤"Actual",这会带回ActualHeightActualWidth
  • 如果ActualHeight为零,使用 给它一个最小高度MinHeight,这样我们至少可以看到一些东西。
  • 如果ActualHeight太大以至于超出屏幕边缘(即 16,000),请使用 给它一个合理的最大高度MaxHeight,这样滚动条就会出现。

Once the scrollbars are appearing, then we can clean it up further:

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

  • Bind the Heightof the StackPanelor Gridto the ActualHeightof the parent.
  • HeightStackPanel或绑定GridActualHeight父项的 。

Finally, put a ScrollViewerinside this StackPanel.

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

Appendix C: Previous Attempt 3

附录 C:之前的尝试 3

It turns out that this can sometimes fail:

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

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

The reason? It the binding fails, the height will be zero and nothing will be seen. The binding can fail if we are binding to an element which is not accessible. The binding will fail if we are going upthe visual tree, then downto a leaf node (e.g. up to the parent grid, then down to the ActualHeightof a row attached to that grid). This is why binding to the ActualWidthof a RowDefinitionsimply won't work.

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

Appendix D: Previous Attempt 4

附录 D:之前的尝试 4

I ended up getting this working by making sure that Height=Autofor all of the parent elements from us to the first <Grid>element in the UserControl.

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

回答by dev hedgehog

You can either set a fix height on your ScrollViewer but then you have to consider that the second row of your grid will have that height too since row's first child will be the ScrollViewer and row's height is auto, or you bind the height of ScrollViewer to another control in your layout. We don't know how your layout looks alike.

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

At the end if you don't like neither of both just set the row's height to * as swiszcz suggested or hack wpf write your own custom panel that will be able to layout everything possible in every parallel universe or something like that. :)

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

回答by Developer

What I discover is that you have to put your ScrollViewerwithin a container that has Height=Autoor you get his parent Heigh Actual Sizeand apply it to that container.

我发现你必须把你的ScrollViewer放在一个容器中,Height=Auto或者你得到他的父母Heigh Actual Size并将它应用到那个容器中。

In my case I have UserControllike

就我而言,我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> 

And I add it dinamically to ContentControlwhich is within a Page.

我将其添加到dinamicallyContentControl这是内部的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>

Notice that Heighof the Rowis *

注意HeighRow*

When I populate ContentControlI use this code in Loadedevent

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

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

And also when ContentControlchanges its size you have to update size of the UserControl.

而且当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;
                }
            }
        }

Enjoy!

享受!

P.S. Acctually I did it for UWP.

PS 实际上我是为 UWP 做的。

回答by cars

I've had similar problem, taking me hours to figure out the solution. What solved it was using a Dockpanel as parent container instead of a StackPanel. Just specify all children to dock to top if the functionality should be similar to vertical stackpanel. Consider using LastChildFill="False" in the Dock XAML which is'n default.

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

So instead of:

所以而不是:

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