如何在标准WPF ListView中启用UI虚拟化

9
我正在使用.NET 4.5/VS2012,我有一个类似以下的ListView:
<ListView 
    VirtualizingPanel.IsContainerVirtualizable="True"
    VirtualizingPanel.IsVirtualizing="True"
    VirtualizingPanel.IsVirtualizingWhenGrouping="True"
    Grid.Row="1"
    Name="eventLogList"
    Margin="5,0,5,0"
    BorderBrush="Black"
    BorderThickness="2"
    ItemsSource="{Binding EventLogs}"
    SelectedItem="{Binding SelectedEventLog}"
    local:ListViewSorter.CustomListViewSorter="EventLogViewer.UI.EventLogItemComparer"
    SelectionMode="Single">

    <ListView.GroupStyle>
        <GroupStyle HidesIfEmpty="False">
            <GroupStyle.ContainerStyle>
                <Style TargetType="GroupItem">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="GroupItem">
                                <Expander IsExpanded="True">
                                    <Expander.Header>
                                        <TextBlock FontSize="20" TextWrapping="Wrap" Margin="0,10,0,5" >
                                        <Bold><TextBlock Text="{Binding Name}"/></Bold> - <TextBlock FontSize="20" Text="{Binding ItemCount}"/> logs
                                    </TextBlock>
                                    </Expander.Header>
                                    <ItemsPresenter/>
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
    </ListView.GroupStyle>
    <ListView.View>
        <GridView>
            <GridViewColumn 
                Header="event id"
                Width="120"
                DisplayMemberBinding="{Binding EventID}" />
            <GridViewColumn 
                Header="level"
                Width="160"
                DisplayMemberBinding="{Binding Level}" />
            <GridViewColumn 
                Header="server" 
                Width="160"
                DisplayMemberBinding="{Binding Server}" />
            <GridViewColumn 
                Header="log name" 
                Width="160"
                DisplayMemberBinding="{Binding LogName}" />
            <GridViewColumn 
                Header="source"
                Width="240"
                DisplayMemberBinding="{Binding Source}" />
            <GridViewColumn 
                Header="logged"
                Width="240"
                DisplayMemberBinding="{Binding Logged}" />
        </GridView>
    </ListView.View>
</ListView>

但是性能仍然没有改善。我找到了一个使用ListBox的示例,但如何虚拟化ListView?我苦苦挣扎。 我听说在先前版本的WPF中,通过分组来关闭虚拟化,但是在.NET 4.5中,WPF具有一个IsVirtualizingWhenGrouping属性,我已将其设置为True
更新:罪魁祸首是自定义样式,在移除它后,列表视图就像涂了黄油一样流畅运行。
3个回答

16

UI虚拟化仅将可见项目存储在内存中,但在数据绑定场景中会将整个数据结构存储在内存中。相比之下,数据虚拟化仅在内存中存储屏幕上可见的数据项。

当ListView和ListBox控件的列表项绑定到数据时,默认情况下启用UI虚拟化。

要了解更多信息,请参阅原始MSDN来源。


6

1
第二个链接并没有太大帮助,因为我预算紧张,无法使用商业软件。至于MSDN讨论,我还在消化中。我确实使用了一些自定义样式,将尝试禁用它们进行测试。 - imgen
2
禁用自定义样式后,问题得到解决。不过正在寻找一种既能兼顾两者的方法。 - imgen

5

我知道这是一个老问题,但我在寻找答案时遇到了它,并想分享一下我发现的内容,以防对其他人有用。我遇到了一个没有虚拟化的ListView控件,与此非常相似。我删除了它上面的自定义样式(在阅读本主题和相关链接后),然后它开始正确地进行虚拟化。

经过大量调查、与默认模板的比较和缩小范围,我发现它是模板内部的ScrollContentPresenter上的“CanContentScroll”属性。我根本没有设置它,在我将它设置为true后,它开始正确地进行虚拟化。我还注意到默认模板中有“CanHorizontallyScroll="False"”和“CanVerticallyScroll="False"”,在我的有限测试中似乎没有什么区别(我确定有人可以介绍一下它们的作用),但我还是将它们留了下来。

这是我的最终样式(请注意,这是从默认样式开始修改的,因此不确定CanContentScroll属性被删除的位置...):

<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}"
   TargetType="ScrollViewer">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ScrollViewer">
                <Grid Background="{TemplateBinding Background}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>

                    <DockPanel Margin="{TemplateBinding Padding}">
                        <ScrollViewer DockPanel.Dock="Top"
                                      HorizontalScrollBarVisibility="Hidden"
                                      VerticalScrollBarVisibility="Hidden"
                                      Focusable="false">
                            <GridViewHeaderRowPresenter Margin="2,0,2,0"
                                                        Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderContainerStyle="{Binding
                         Path=TemplatedParent.View.ColumnHeaderContainerStyle,
                         RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderTemplate="{Binding
                         Path=TemplatedParent.View.ColumnHeaderTemplate,
                         RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderTemplateSelector="{Binding 
                         Path=TemplatedParent.View.ColumnHeaderTemplateSelector,
                         RelativeSource={RelativeSource TemplatedParent}}"
            AllowsColumnReorder="{Binding
                         Path=TemplatedParent.View.AllowsColumnReorder,
                         RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderContextMenu="{Binding
                         Path=TemplatedParent.View.ColumnHeaderContextMenu,
                         RelativeSource={RelativeSource TemplatedParent}}"
            ColumnHeaderToolTip="{Binding
                         Path=TemplatedParent.View.ColumnHeaderToolTip,
                         RelativeSource={RelativeSource TemplatedParent}}"
            SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                        </ScrollViewer>

                        <ScrollContentPresenter Name="PART_ScrollContentPresenter"
                                                KeyboardNavigation.DirectionalNavigation="Local" 
                                                CanContentScroll="True"
                                                CanHorizontallyScroll="False"
                                                CanVerticallyScroll="False"/>
                    </DockPanel>

                    <ScrollBar Name="PART_HorizontalScrollBar"
                               Orientation="Horizontal"
                               Grid.Row="1"
                               Maximum="{TemplateBinding ScrollableWidth}"
                               ViewportSize="{TemplateBinding ViewportWidth}"
                               Value="{TemplateBinding HorizontalOffset}" 
                               Style="{StaticResource StScrollBarNoMargin}"
                               Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>

                    <ScrollBar Name="PART_VerticalScrollBar"
                               Grid.Column="1"
                               Style="{StaticResource StScrollBarNoMargin}"
                               Maximum="{TemplateBinding ScrollableHeight}"
                               ViewportSize="{TemplateBinding ViewportHeight}"
                               Value="{TemplateBinding VerticalOffset}"
                               Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>

                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接