我了解到,如果ScrollViewer
所在的网格行的高度设置为Auto
,则垂直滚动条将不会生效,因为{{1}的实际大小可以大于视线中的高度。因此,为了使滚动条工作,我应该将高度设置为固定数字或星形高度
但是,我现在有了这个要求,我有两个不同的视图驻留在两个网格行中,并且我有一个切换按钮可以在这两个视图之间切换:当显示一个视图时,另一个视图隐藏/消失。所以我定义了两行,两个高度都设置为ScrollViewer
。我将每行中视图的可见性绑定到我的ViewModel的布尔属性(一个从Auto
转换为True
,另一个从Visible
转换为True
。这个想法是当一个视图的可见性为Collapsed
时,网格行/视图的高度将自动更改为0.
视图显示/隐藏工作正常。但是,在一个视图中,我有Collapsed
,正如我所提到的,当行高设置为ScrollViewer
时,它不起作用。任何人都能告诉我如何在Auto
自动工作的同时满足这样的要求吗?我想我可以在代码隐藏中设置高度。但由于我使用的是MVVM,因此需要额外的通信/通知。有没有更简单的方法呢?
答案 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绝对是非常宝贵的:
如果任何XAML元素的“高度”为0
或NaN
,您可以使用以下方法之一将其设置为:
Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UIElement}}"
VerticalAlignment="Stretch"
Height="Auto"
提示:如果您是VerticalAlignment="Stretch"
Grid
的孩子,请使用<RowDefinition Height="*">
,如果不起作用则使用Binding RelativeSource...
。
如果您有兴趣,以下是我之前解决此问题的所有尝试:
也可以使用:
Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}"
有用信息:请参阅WPF - Auto Height in combination with MaxHeight。
如果似乎没有任何效果,可能是因为父项的ActualHeight
为0(因此没有任何内容可见)或巨大(因此滚动查看器永远不需要出现)。如果存在深度嵌套的网格,并且滚动查看器位于底部,那么这就更成问题了。
ActualHeight
的{{1}}。在属性中,按照StackPanel
字词进行过滤,这会带回"Actual"
和ActualHeight
。ActualWidth
为零,请使用ActualHeight
给它一个最小高度,这样我们至少可以看到一些东西。MinHeight
太大以至于它离屏幕边缘(即16,000),请使用ActualHeight
给它一个合理的最大高度,这样就会出现滚动条。滚动条出现后,我们可以进一步清理它:
MaxHeight
或Height
的{{1}}绑定到父级的StackPanel
。最后,将Grid
放入此ActualHeight
。
事实证明,这有时会失败:
ScrollViewer
原因?绑定失败,高度为零,没有任何东西可见。如果我们绑定到不可访问的元素,绑定可能会失败。如果我们将StackPanel
视觉树,然后Height="{Binding Path=ActualHeight, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=StackPanel}}"
传递给叶节点(例如,直到父网格,然后直到连接到该网格的行的up
,则绑定将失败格)。这就是为什么绑定到down
的{{1}}根本不起作用。
我最终通过确保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>