如何让 WPF 网格行具有自动高度而不超过窗口高度?

Dre*_*eep 5 c# wpf xaml itemscontrol

我有一个窗口,其中包含一个ItemsControl内部可以包含可变数量控件的窗口。为了考虑到超出窗口高度的情况,我将其包装在 a 中ScrollViewer,以便当项目数量超过可用高度时会显示滚动条。

现在的问题是,有时不会显示任何内容ItemsControl,有时却会显示。因此,我将网格行的高度设置为 ,Auto以允许 网格ItemsControl行在空时消失,或在需要时增长。但是,这意味着该行将占用所需的高度,即使这超出了窗口高度,并且永远不会显示垂直滚动条。

以下是演示该问题的示例窗口的一些 XAML...

<Window x:Class="DuplicateCustomerCheck.TestScrollViewerWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Test Scroll Viewer Window"
        Height="450"
        Width="200">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <TextBox Name="N"
             TextChanged="TextBoxBase_OnTextChanged"
             Grid.Row="0"
             Margin="3" />

    <Grid Margin="3"
          Grid.Row="1">
      <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
      </Grid.RowDefinitions>
      <TextBlock Text="Possible duplicate of..."
                 Margin="3" />
      <ScrollViewer VerticalScrollBarVisibility="Visible"
                    Grid.Row="1">

        <ItemsControl Name="MatchingNames"
                      ItemsSource="{Binding MatchingNames, Mode=TwoWay}">
          <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
              <StackPanel Orientation="Vertical" />
            </ItemsPanelTemplate>
          </ItemsControl.ItemsPanel>

          <ItemsControl.ItemTemplate>
            <DataTemplate>
              <Button Content="{Binding Item}" />
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
      </ScrollViewer>
    </Grid>

    <TextBlock Grid.Row="2"
               Margin="3"
               Text="Stuff at the bottom" />
  </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

出于演示目的,这里是按钮的事件处理程序,它允许我测试不同数量的项目(请注意,这是 noddy 代码,因此没有错误检查等)...

private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) {
  MatchingNames.ItemsSource = Enumerable
    .Range(0, int.Parse(N.Text))
    .Select(n1 => new {
      Item = "Button " + n1
    });
}
Run Code Online (Sandbox Code Playgroud)

如果我将第二个网格行的高度更改为,*那么它可以正常工作,但这意味着它ItemsControl永久可见,这是我不想要的。仅当其中有某些项目时才应显示它。

我尝试了这篇博客文章ScrollViewerMaxSizeBehavior中的行为(代码在这里),但它没有任何区别。

任何人都知道我如何允许它ItemsControl占据所需的垂直空间,包括零,但不长得比窗口能容纳的高度?

Mar*_*kus 1

这种情况仅通过 XAML 来解决是很棘手的。我会通过一些计算来解决它\xe2\x80\xa6

\n\n
<Grid x:Name="MyGrid">\n    <Grid.RowDefinitions>\n        <RowDefinition Height="Auto" />\n        <RowDefinition Height="Auto" MaxHeight="{Binding Row2MaxHeight}"/>\n        <RowDefinition Height="Auto" />\n    </Grid.RowDefinitions>\n\n    <TextBox Name="N" TextChanged="TextBoxBase_OnTextChanged" Grid.Row="0" Margin="3" />\n\n    <Grid Margin="3" Grid.Row="1" x:Name="MyInnerGrid">\n        <Grid.RowDefinitions>\n            <RowDefinition Height="Auto"/>\n            <RowDefinition Height="Auto" />\n        </Grid.RowDefinitions>\n        <TextBlock Text="Possible duplicate of..." Margin="3" />\n        <ScrollViewer Grid.Row="1" MaxHeight="{Binding Row2MaxHeightInner}">\n            <ItemsControl Name="MatchingNames" ItemsSource="{Binding MatchingNames, Mode=TwoWay}">\n                <ItemsControl.ItemsPanel>\n                    <ItemsPanelTemplate>\n                        <StackPanel Orientation="Vertical" />\n                    </ItemsPanelTemplate>\n                </ItemsControl.ItemsPanel>\n\n                <ItemsControl.ItemTemplate>\n                    <DataTemplate>\n                        <Button Content="{Binding Item}" />\n                    </DataTemplate>\n                </ItemsControl.ItemTemplate>\n            </ItemsControl>\n        </ScrollViewer>\n    </Grid>\n\n    <TextBlock Grid.Row="2"\n               Margin="3"\n               Text="Stuff at the bottom" />\n</Grid>\n
Run Code Online (Sandbox Code Playgroud)\n\n

和代码:

\n\n
public partial class MainWindow : Window, INotifyPropertyChanged {\n    public MainWindow() {\n        InitializeComponent();\n\n        DataContext = this;\n        SizeChanged += SizeWasChanged;\n    }\n\n    private void SizeWasChanged(object sender, SizeChangedEventArgs e) {\n        OnPropertyChanged(nameof(Row2MaxHeight));\n        OnPropertyChanged(nameof(Row2MaxHeightInner));\n    }\n\n    public double Row2MaxHeight => ActualHeight - MyGrid.RowDefinitions[0].ActualHeight - MyGrid.RowDefinitions[2].ActualHeight - 50; //50 or something is around the Size of the title bar of the window\n    public double Row2MaxHeightInner => Row2MaxHeight - MyInnerGrid.RowDefinitions[0].ActualHeight - 6; //6 should match the margin of the scrollviewer\n\n    private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) {\n        MatchingNames.ItemsSource = Enumerable\n            .Range(0, int.Parse(N.Text))\n            .Select(n1 => new {\n                Item = "Button " + n1\n            });\n        OnPropertyChanged(nameof(Row2MaxHeight));\n        OnPropertyChanged(nameof(Row2MaxHeightInner));\n    }\n\n    public event PropertyChangedEventHandler PropertyChanged;\n\n    [NotifyPropertyChangedInvocator]\n    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {\n        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n