首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在不超过窗口高度的情况下使WPF网格行具有自动高度?

如何在不超过窗口高度的情况下使WPF网格行具有自动高度?
EN

Stack Overflow用户
提问于 2018-12-28 02:54:02
回答 2查看 1K关注 0票数 3

我有一个包含ItemsControl的窗口,其中可以包含数量可变的控件。为了解决窗口高度无法容纳的情况,我将其包装在一个ScrollViewer中,这样当项目的数量超过可用高度时,就会显示滚动条。

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

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

代码语言:javascript
运行
复制
<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>

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

代码语言:javascript
运行
复制
private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) {
  MatchingNames.ItemsSource = Enumerable
    .Range(0, int.Parse(N.Text))
    .Select(n1 => new {
      Item = "Button " + n1
    });
}

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

我尝试了this blog post (code here)的ScrollViewerMaxSizeBehavior行为,但没有任何区别。

有没有人知道我怎么才能让ItemsControl根据需要占用尽可能多的垂直空间,包括零空间,但又不会超出窗口的高度呢?

EN

回答 2

Stack Overflow用户

发布于 2018-12-28 05:23:57

仅使用XAML就很难解决这种情况。我会用一些计算来解决它,…

代码语言:javascript
运行
复制
<Grid x:Name="MyGrid">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" MaxHeight="{Binding Row2MaxHeight}"/>
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

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

    <Grid Margin="3" Grid.Row="1" x:Name="MyInnerGrid">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock Text="Possible duplicate of..." Margin="3" />
        <ScrollViewer Grid.Row="1" MaxHeight="{Binding Row2MaxHeightInner}">
            <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>

和代码:

代码语言:javascript
运行
复制
public partial class MainWindow : Window, INotifyPropertyChanged {
    public MainWindow() {
        InitializeComponent();

        DataContext = this;
        SizeChanged += SizeWasChanged;
    }

    private void SizeWasChanged(object sender, SizeChangedEventArgs e) {
        OnPropertyChanged(nameof(Row2MaxHeight));
        OnPropertyChanged(nameof(Row2MaxHeightInner));
    }

    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
    public double Row2MaxHeightInner => Row2MaxHeight - MyInnerGrid.RowDefinitions[0].ActualHeight - 6; //6 should match the margin of the scrollviewer

    private void TextBoxBase_OnTextChanged(object sender, TextChangedEventArgs e) {
        MatchingNames.ItemsSource = Enumerable
            .Range(0, int.Parse(N.Text))
            .Select(n1 => new {
                Item = "Button " + n1
            });
        OnPropertyChanged(nameof(Row2MaxHeight));
        OnPropertyChanged(nameof(Row2MaxHeightInner));
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
票数 0
EN

Stack Overflow用户

发布于 2019-01-23 10:06:14

在我发布a similar question之后,我发现了这个问题。尽管您提到“因此,我将网格行的高度设置为自动,以允许ItemsControl在为空时消失,或在需要时增长”,但至少我认为我们是在问同样的问题。但是,当我尝试您的示例时,即使在空的情况下,ScrollViewer仍然显示其滚动条,并且它正在占用空间。

无论如何,mami answered my question,虽然他们的答案对我并不完全有效,但它和Markus's answer一起让我想到了my own answer

不过,我的答案不太适合您的情况,因为我的答案假设ItemsControlGrid行中唯一的东西,而您的行中也有“...的可能副本”TextBlock。我调整了我的答案,以考虑到TextBlock的大小,但它并不像我希望的那样干净。作为一种可能的优化,在计算ItemsControlItem的总高度时,一旦高度变得“足够大”(例如,大于Window的高度),你可以提前退出。这样,如果你有数千个项目,但实际上只有几十个项目可以在屏幕上显示,你可以只得到几十个项目的高度,而不是数千个项目的高度。

无论如何,它可能会给您一些想法:)

XAML:

代码语言:javascript
运行
复制
<Window x:Class="WpfApp1.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.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" MaxHeight="{Binding ItemMaxHeight,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}"/>
            <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 Name="tb" Text="Possible duplicate of..."
                 Margin="3" />
            <ScrollViewer VerticalScrollBarVisibility="Visible"
                    Grid.Row="1">

                <ItemsControl Name="MatchingNames"
                      ItemsSource="{Binding MatchingNames, Mode=TwoWay}"
                      SizeChanged="MatchingNames_SizeChanged">
                    <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>

代码隐藏:

代码语言:javascript
运行
复制
public partial class MainWindow : Window, INotifyPropertyChanged
{
    public MainWindow()
    {
        InitializeComponent();
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

    public double ItemMaxHeight
    {
        get
        {
            if (MatchingNames == null)
                return double.PositiveInfinity;

            double height = 0.0;
            var icg = MatchingNames.ItemContainerGenerator;
            for (int i = 0; i < MatchingNames.Items.Count; i++)
                height += (icg.ContainerFromIndex(i) as FrameworkElement).ActualHeight;

            return height 
                + tb.Margin.Top + tb.ActualHeight + tb.Margin.Bottom
                + 6.0; // 6 should match the margin of the scrollviewer
        }
    }

    private void MatchingNames_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (e.HeightChanged)
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ItemMaxHeight"));
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53949585

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档