扩展器,网格和ListBox =没有虚拟化

时间:2011-07-23 14:09:10

标签: wpf xaml .net-4.0 grid

我在一个网格中有一个ListBox,在Expander中。 ListBox绑定到IList。

当我第一次展开Expander控件时,ListBox处理IList中的所有项目(可能是数千个),而不是仅处理屏幕上可见的项目。

但是,如果我修复了ListBox控件的高度,它的行为与预期的一样,只访问IList中可见的那些项。

实际上,虚拟化不起作用,但我认为这与ListBox在准备内容项时无法确定高度有关。

XAML基本上如下(为简化起见,删除了一些内容)......

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Expander ExpandDirection="Right"
              Grid.Column="0"
              Grid.Row="0"
              Grid.RowSpan="2"
              Header="Documents"
              IsExpanded="False">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Grid Grid.Row="0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="50" />
                </Grid.RowDefinitions>
            </Grid>
            <ListBox Name="listBox"
                     Grid.Row="1"
                     ItemsSource="{Binding Path=Items}"
                     SelectedIndex="{Binding Path=SelectedIndex}"
                     SelectedItem="{Binding Path=SelectedItem}"
                     SelectionMode="Single"
                     Width="250">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="*" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="auto" />
                            </Grid.RowDefinitions>
                            <TextBlock Grid.Column="0"
                                       Style="{StaticResource prompt}">
                                <TextBlock.Text>
                                    <MultiBinding StringFormat="{}{0}{1:00000}">
                                        <Binding Path="..."
                                                 FallbackValue="0" />
                                        <Binding Path="..." />
                                    </MultiBinding>
                                </TextBlock.Text></TextBlock>
                            <TextBlock Grid.Column="1"
                                       Style="{StaticResource prompt}">
                                <TextBlock.Text>
                                    <Binding Path="ItemCount"
                                             StringFormat="{}{0} Items"
                                             FallbackValue="" />
                                </TextBlock.Text></TextBlock>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>
    </Expander>
    <v:DocumentView x:Name="documentView"
                    Grid.Column="1"
                    Grid.Row="0"
                    DocumentID="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:BatchView}}, Path=ViewModel.SelectedItem.ID}"
                    IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type v:BatchView}}, Path=IsActive}" />
    <StackPanel Grid.Column="1"
                Grid.Row="1"
                Style="{StaticResource buttonStackStyle}">
        <Button Command="{Binding Path=PreviousCommand}"
                Style="{StaticResource previousButtonStyle}" />
        <Button Command="{Binding Path=NextCommand}"
                Style="{StaticResource nextButtonStyle}" />
    </StackPanel>
</Grid>

有人可以建议我如何将ListBox的高度设置为Grid.Row父级的ActualHeight吗?或者,任何人都可以提供更好的解决方案吗?

感谢。

1 个答案:

答案 0 :(得分:1)

简短版本:从扩展程序中删除Grid.RowSpan

长版:

背景(以非常广泛的笔触)

当您定义RowDefinition的高度时,可能会发生三件事,具体取决于您最终使用的单位类型:

  1. Pixel - 放置在该行中的任何UIElement都会将定义的行高传递给元素的MeasureArrange方法。
  2. 自动 - 网格将通过无穷大作为Measure的高度,然后element.DesiredSize.Height作为Arrange的高度。
  3. Star - 网格将以像素和auto为单位考虑所有行的高度;计算从其可用高度留下的高度,并将其除以为所有行定义的“总星数” - 这是一个星的高度;然后,根据星形定义的乘数分配每个行高;此高度将传递给MeasureArrange方法。
  4. 相同的逻辑仅适用于有关宽度而非高度的列定义。

    因此,星形定义是“停止”元素,像素定义也是“停止”但它可以在渲染视图之外,并且自动定义“让”元素成为它想要的大小。< / p>

    所有这些逻辑都是递归的,所以你需要从两个方向思考(下面的解释)。

    在你的情况下

    在一个方向。 ListBox处于星形行中,因此它将被停止。父网格也会停止(因为扩展器的模板使用的DockPanel也是“停止面板”)。扩展器被定义为以星形行开始,但它跨越到自动行 - 这意味着它将被允许在高度上增长直到无限远。哎呀...时间逆转。

    现在反方向。扩展器未停止,子网格未停止(因为网格假定它具有无限高度可用),因此列表框未停止,列表框模板中的ScrollViewer未停止,因此{ {1}}是无限的,对于排列项目的ViewportHeight(并且是滚动查看器的子项),这意味着所有项目都在视图中==呈现所有元素。

    对于具有默认模板的WPF窗口,您始终可以假定窗口正在停止其子元素。因此,如果删除行范围定义尚未解决问题,请继续遍历,直到找到另一个未停止其子高度的元素并更改其定义或更改面板以停止高度增长到无穷大(滚动查看器因此而臭名昭着创建这些行为,尤其是隐藏在模板中的行为。)