如何让网格行高仅占用所需的空间,又受其容器中可用空间的限制?

时间:2019-01-22 05:04:19

标签: wpf layout

我想要一个页眉,然后在其下的ScrollViewer和一个ItemsControl,然后在该页脚之下。像这样:

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid ShowGridLines="True">
        <Grid.Resources>
            <Style TargetType="TextBlock">
                <Setter Property="FontSize" Value="36"/>
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0">Header</TextBlock>
        <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
            <ItemsControl>
                <ItemsControl.Items>
                    <TextBlock>Item 1</TextBlock>
                    <TextBlock>Item 2</TextBlock>
                    <TextBlock>Item 3</TextBlock>
                    <TextBlock>Item 4</TextBlock>
                    <TextBlock>Item 5</TextBlock>
                </ItemsControl.Items>
            </ItemsControl>
        </ScrollViewer>
        <TextBlock Grid.Row="2">Footer</TextBlock>
    </Grid>
</Window>

上面几乎是我想要的,除了中间的行是贪婪的;即使窗户很高,它也会占用尽可能多的空间,将页脚推到窗户底部。

如果我将中间行的定义更改为Height="Auto",即使该空间不可用,它也会精确地占用它所需的空间量,因此ScrollViewer永远不会显示滚动条,如果窗口不够高,页脚将从窗口底部迷失。

我如何做到这一点,如果窗口足够高,足以容纳所有内容,则页脚位于ItemsControl的正下方,但是如果窗口不够高,则ScrollViewer显示滚动条,页脚在窗口的底部?

我不一定非要使用Grid来执行此操作,但是我没有找到其他Panel来满足我的要求。例如,DockPanel的标头设置为DockPanel.Dock="Top",页脚设置为DockPanel.Dock="Bottom",而填充其余部分的ItemsControl的行为完全相同。

我尝试过的其他一些东西:

  • 在页脚VerticalAlignment="Stretch"上设置TextBlock:不变。
  • 使页脚行Height="*":仍然不是我想要的;页脚和ItemsControl的高度相同,因此页脚大部分时间会占用太多空间,或者如果您将窗口做得非常短,则会从窗口底部移出。

2 个答案:

答案 0 :(得分:1)

也许有一种更优雅的方法,但是至少在以下情况下,以下内容对我有用。这是关于计算ItemsControl中所有项目的实际高度,并根据需要调整Grid的行高。

<Grid ShowGridLines="True" Loaded="Grid_Loaded">
    <Grid.Resources>
        <Style TargetType="TextBlock">
            <Setter Property="FontSize" Value="36"/>
        </Style>
    </Grid.Resources>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*" Name="middlerow"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0">Header</TextBlock>
    <ScrollViewer Name="scv" Grid.Row="1" VerticalScrollBarVisibility="Auto">
        <ItemsControl x:Name="items" Height="Auto" VerticalAlignment="Top">
            <ItemsControl.Items>
                <TextBlock>Item 1</TextBlock>
                <TextBlock>Item 2</TextBlock>
                <TextBlock>Item 3</TextBlock>
                <TextBlock>Item 4</TextBlock>
            </ItemsControl.Items>
        </ItemsControl>
    </ScrollViewer>
    <TextBlock Grid.Row="2">Footer</TextBlock>
</Grid>

在后面的代码中:

private void Grid_Loaded(object sender, RoutedEventArgs e)
    {
        double height = 0;
        foreach (var item in items.Items)
        {
            height += (item as TextBlock).ActualHeight;
        }
        if (height < scv.ActualHeight)
        {
            middlerow.MaxHeight = height;
        }
    }

答案 1 :(得分:0)

感谢mami's answer的实现方法概念,感谢Markus's answer的约束行的MaxHeight的想法。

XAML:

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid ShowGridLines="True">
        <Grid.Resources>
            <Style TargetType="TextBlock">
                <Setter Property="FontSize" Value="36"/>
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*" MaxHeight="{Binding ItemsMaxHeight,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0">Header</TextBlock>
        <ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
            <ItemsControl Name="ic" SizeChanged="Ic_SizeChanged">
                <ItemsControl.Items>
                    <TextBlock>Item 1</TextBlock>
                    <TextBlock>Item 2</TextBlock>
                    <TextBlock>Item 3</TextBlock>
                    <TextBlock>Item 4</TextBlock>
                    <TextBlock>Item 5</TextBlock>
                </ItemsControl.Items>
            </ItemsControl>
        </ScrollViewer>
        <TextBlock Grid.Row="2">Footer</TextBlock>
    </Grid>
</Window>

后面的代码:

Imports System.ComponentModel

Class MainWindow
    Implements INotifyPropertyChanged

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Public ReadOnly Property ItemsMaxHeight As Double
        Get
            Dim Height = 0.0
            Dim icg = ic.ItemContainerGenerator

            For i = 0 To ic.Items.Count - 1
                Height += DirectCast(icg.ContainerFromIndex(i), FrameworkElement).ActualHeight
            Next

            Return Height + 6.0 ' 6.0 to account for the size of borders? Not sure :(
        End Get
    End Property

    Private Sub Ic_SizeChanged(sender As Object, e As SizeChangedEventArgs)
        If e.HeightChanged Then
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ItemsMaxHeight"))
        End If
    End Sub
End Class

在此示例XAML中,我不需要将MaxHeight增加6,但是在我的实际应用中,如果我不添加任何额外内容,则ScrollViewer始终显示一个滚动条,该滚动条可以滚动了一点。不确定导致差异的原因。