扩展器、网格和扩展器列表框 = 无虚拟化

发布于 2024-11-25 20:06:34 字数 4341 浏览 1 评论 0原文

我有一个列表框,在网格内,在扩展器内。 ListBox 绑定到 IList。

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

但是,如果我修复 ListBox 控件的高度,它的行为将按预期进行,并且仅访问 IList 中可见的那些项目。

实际上,虚拟化不起作用,尽管我认为这与列表框在准备内容项时无法确定高度更相关。

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 吗?或者,有人可以提供更好的解决方案吗?

谢谢。

I have a ListBox, inside a Grid, inside an Expander. The ListBox is bound to an IList.

When I expand the Expander control for the first time, the ListBox processes all of the items in the IList (which can be thousands) instead of only processing the items that would be visible on the screen.

If however I fix the height of the ListBox control, it behaves as expected and only accesses those items in the IList that will be visible.

Effectively, the Virtualization is not working, though I believe that this is more related to the ListBox not being able to determine a height when the content items are being prepared.

The XAML is basically as follows (some stuff removed for simplification)...

<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>

Can anybody suggest how I might set the Height of the ListBox to the ActualHeight of the Grid.Row parent? Alternatively, can anybody provide a better solution?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

李不 2024-12-02 20:06:34

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

长版本:

背景:(非常粗略)

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

  1. Pixel - 放置在该行中的任何 UIElement 都会将定义的行高传递给元素的 Measure 和 Arrange 方法。
  2. 自动 - 网格将传递无穷大作为 Measure 的高度,然后传递 element.DesiredSize.Height 作为 Arrange 的高度。
  3. Star - 网格会考虑所有行的高度,单位为像素和auto;计算其可用高度剩余的高度,并将其除以为所有行定义的“星星总数” - 这是一颗星星的高度;然后,根据星形定义的乘数来分配每个行的高度;该高度将传递给 MeasureArrange 方法。

相同的逻辑仅适用于有关宽度而不是高度的列定义。

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

所有这些逻辑都是递归的,因此您需要从两个方向思考(解释如下)。

就你的情况

朝一个方向。 ListBox 位于星形行中,因此它将被停止。父网格也被停止(因为扩展器的模板使用也是“停止面板”的 DockPanel)。扩展器被定义为从星形行开始,但它跨越到自动行 - 这意味着它将允许其高度增长“直到无穷大”。哎呀……是时候逆转了。

现在反方向。扩展器没有停止,子网格没有停止(因为网格假设它有无限的可用高度),因此列表框没有停止,列表框模板中的 ScrollViewer 没有停止所以它的 ViewportHeight 是无限的,对于排列项目(并且是滚动查看器的子级)的 VirtualizingStackPanel 来说,这意味着所有项目都在视图中 == 渲染所有元素。

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

Short version: remove Grid.RowSpan from the expander.

Long version:

Background: (in really broad strokes)

When you define the height of RowDefinition three things can happen, depending what type of units you end up using:

  1. Pixel - any UIElement that is placed in that row will have the defined row height passed to the Measure and Arrange methods of the element.
  2. Auto - the grid will pass infinity as the height for Measure and then element.DesiredSize.Height for Arrange.
  3. Star - the grid will consider the heights of all rows with units of pixels and auto; calculate the height that is left from its available height and divide it by the "total of stars" that were defined for all the rows - this is the height of one star; then, each row height is assigned depending on the multiplier for its star definition; this height is passed to the Measure and Arrange methods.

Same logic is applied to column definitions only regarding width instead of height.

So, star definition is "stopping" the element, pixel definition is also "stopping" but it can be outside of the rendered view and auto definition is "letting" the element to be what ever size it wants to be.

All this logic is recursive so you need to think in two directions (explanation below).

In your case

In one direction. The ListBox is in a star row so it'll be stopped. The parent grid is also stopped (since the template for expander uses DockPanel that is also a "stopping panel"). The expander is defined to begin in a star row but it spans to an auto row - this means that it will be allowed to grow in height 'till infinity. Oops...time to reverse.

Now the reverse direction. The expander is not stopped, the child grid is not stopped (since the grid assumes it has infinite height available), thus the list box is not stopped, the ScrollViewer in the template of list box is not stopped so it's ViewportHeight is infinite, for the VirtualizingStackPanel that arranges the items (and is a child of the scroll viewer) this means all items are in the view == render all elements.

For a WPF window with default template, you can always assume the window is stopping its child element. So if removing the row span definition has not resolved the issue, continue traversing up until you find another element that is not stopping its child height and change its definitions or change the panel to stop the height from growing to infinity (scroll viewers are notorious for creating these behaviours, especially the ones that are hidden in templates).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文