我有一个窗口,其中包含一个ItemsControl
,该窗口中可以包含可变数量的控件。为了解决超出窗口高度的情况,我将其包裹在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>
出于演示目的,这里是按钮的事件处理程序,允许我测试不同数量的项目(请注意,这是点头代码,因此没有错误检查等)...
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
占用所需的垂直空间,包括零,但又不会变得超出窗口的高度吗?
答案 0 :(得分:0)
仅使用XAML可以解决这种情况。我将通过一些计算来解决它……
<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>
和代码:
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));
}
}
答案 1 :(得分:0)
发布a similar question后,我发现了这个问题。至少我想,我们在问同样的事情,尽管您提到“因此,我将网格行的高度设置为“自动”,以使ItemsControl在为空时消失,或在需要时增长。”但是,当我尝试采样时,即使是空的,ScrollViewer
仍在显示其滚动条,并且占用了空间。
无论如何,mami answered my question尽管他们的答案对我来说并不完全有效,但它与Markus's answer一起使我提出了my own answer。
但是,我的答案与您的情况不太吻合,因为我假设ItemsControl
是Grid
行中的唯一内容,而您却拥有“ ...的可能重复项”行中的TextBlock
。我调整了答案以考虑TextBlock
的大小,但是它并不像我希望的那样干净。作为一种可能的优化,在计算ItemsControl
的{{1}}的总高度时,一旦高度达到“足够大”(例如,比Item
大),您可能会提早退出。的高度)。这样,如果您有成千上万个项目,而实际上只有几十个可以放到屏幕上,那么您只需获得几十个而不是数千个的高度即可。
无论如何,也许它会给您一些想法:)
XAML:
Window
后面的代码:
<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>