none
Issue with Binding ListView to CollectionViewSource

    Question

  • Good afternoon,

    I am currently binding a ListView to a CollectionViewSource called "SSource" whose data is stored in a DataTable called "dt".

    ((CollectionViewSource)this
    .Resources["SSource"
    ]).Source = dt.DefaultView;

    I found that after the above statement has been executed and while the ListView is being rendered:
    - if the DataTable has an obscene number of rows (e.g. 30,000), an OutOfMemory Exception is thrown, and
    - if the DataTable has more than about 1500 rows, there is a significant wait time for rendering the ListView control.

    Is there a more time efficient way of loading grouped data to a ListView? Why would an OutOfMemory exception be thrown for only 30,000 records?

    Any help is greatly appreciated.

    Thanks,

    J.D.
    Wednesday, August 26, 2009 7:00 PM

Answers

  • Hi JD86,

    I have performed a test with your code. Indeed, the application consumed a lot of memory after it started up.

    The problem is actually caused by the group functionality in your program. The group caused the default Virtualizing turned off. So the ListView keep on creating UI elements for all the items, thus the memory is used heavily and the performance is greatly impacted.

    So in this case, it would be better not to use group functionality since there are large sets of data in the underlying source. 

    Possibly, if the large set data could be grouped into small parts (I mean the records in a group is relative small, maybe only 100), you can consider using LINQ to group the data and customizing an expander. After a click on the expander, we can load the specified group into view. That is we can load the data on demand.

    For dealing with large sets of items with ItemsControl in WPF, you can refer to this thread on how to fine-tune the performance with data virtualizing. You may especially need to read the PDF version in that thread.

    Feel free to let me know if there’s any problem.

    Best regards,
    Bruce Zhou


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Bruce.Zhou Thursday, September 03, 2009 9:23 AM
    Wednesday, September 02, 2009 6:42 AM

All replies

  • Well, for starters, it's very unlikely that you'll be displaying 30K items in the UI. When you set the source of the ListView to a list, the actual UIElements are rendered for all the items in that list. Consider the following options:
    1. Page your original source.
    2. Set ListView.ItemsPanel to a VirtualizingStackPanel. The WPF way.
    Bigsby, Lisboa, Portugal - O que for, quando for, é que será o que é... http://bigsby.eu
    Thursday, August 27, 2009 1:20 PM
  • The default for ListView.ItemsPanel is already VIrtualizingStackPanel, and I've tried adding VirtualizingStackPAanel elements around both my ItemsPresenter as well as my GridView neither of which helped the rendering issue.

    One thing of note is, during the rendering I'm using a grouping which is grouping the 30k+ records, could this be my bottle neck?  Is there anyway to do this grouping prior to display and still utilize expanders and the like?

    Unfortunately there are occasions we display upwards of 30,000 rows which are grouped by expanders, and this is a necessary evil and was a project requirement.

    Here is the XAML, perhaps you can see where we went awry.

    Thanks!

    J.D.

             <CollectionViewSource x:Key='SSource'>
                <CollectionViewSource.GroupDescriptions>
                    <d:PropertyGroupDescription PropertyName="Vendor" />
                    <d:PropertyGroupDescription PropertyName="Part" />
                </CollectionViewSource.GroupDescriptions>
            </CollectionViewSource>

              <ListView BorderThickness="0" ItemsSource="{Binding Source={StaticResource SSource}}" Margin="0" Name="lv_S" VerticalAlignment="Bottom" Height="485">
                    <ListView.GroupStyle>
                        <GroupStyle>
                            <GroupStyle.ContainerStyle>
                                <Style TargetType="{x:Type GroupItem}">
                                    <Setter Property="Margin" Value="0,0,0,5"/>
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                                <Expander Style="{DynamicResource ExpanderAnimateStyle}" IsExpanded="False" BorderBrush="SlateGray" BorderThickness="0,0,0,1">
                                                    <Expander.Background>
                                                        <RadialGradientBrush PresentationOptions:Freeze="true">
                                                            <RadialGradientBrush.RelativeTransform>
                                                                <TransformGroup>
                                                                    <ScaleTransform ScaleX="1.702" ScaleY="2.243"/>
                                                                    <SkewTransform AngleX="0" AngleY="0"/>
                                                                    <RotateTransform Angle="0"/>
                                                                    <TranslateTransform X="-0.368" Y="-0.152"/>
                                                                </TransformGroup>
                                                            </RadialGradientBrush.RelativeTransform>
                                                            <RadialGradientBrush.GradientStops>
                                                                <GradientStop Color="Gainsboro" Offset="0"/>
                                                                <GradientStop Color="Cornsilk" Offset="1"/>
                                                            </RadialGradientBrush.GradientStops>
                                                        </RadialGradientBrush>
                                                    </Expander.Background>
                                                    <Expander.Header>
                                                        <DockPanel>
                                                            <StackPanel Orientation="Horizontal">
                                                                <TextBlock FontWeight="Bold" Text="{Binding Path=Name}" Margin="5,0,0,0"/>
                                                            </StackPanel>
                                                        </DockPanel>
                                                    </Expander.Header>
                                                    <Expander.Content>
                                                        <ItemsPresenter />
                                                    </Expander.Content>
                                                </Expander>
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </GroupStyle.ContainerStyle>
                        </GroupStyle>
                    </ListView.GroupStyle>
                    <ListView.View>
                    <GridView ColumnHeaderContainerStyle="{StaticResource colHeaderStyle}">
                        <GridViewColumn DisplayMemberBinding="{Binding Path=WeekBegin}" Width="75">Week Begin</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=EstimatedUsage}" Width="80">Demand</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=IdealInventory}" Width="80">Ideal</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=CumulativeInventory}" Width="80">Cumulative</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=CustomerPO}" Width="80">Cust PO</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=CustomerPOLine}" Width="80">Cust PO Line</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=InboundPO}" Width="80">Inbound PO</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=InboundPOQuantity}" Width="100">Inbound PO Qty</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=InboundPOShipMethod}" Width="100">Ship Method</GridViewColumn>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=DockDate}" Width="75">Dock Date</GridViewColumn>
                        <GridViewColumn Width="90" Header="If Buy Qty">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate x:Name="ifBuyTemp">
                                    <TextBox Text="{Binding Path=IfBuyQty}" PreviewMouseDown="txtIfBuy_PreviewMouseDown" LostKeyboardFocus="txtIfBuy_LostKeyboardFocus
                                                       Width="75" Name="txtIfBuy" />
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>
    Thursday, August 27, 2009 3:45 PM
  • Hi JD86,

    I have performed a test with your code. Indeed, the application consumed a lot of memory after it started up.

    The problem is actually caused by the group functionality in your program. The group caused the default Virtualizing turned off. So the ListView keep on creating UI elements for all the items, thus the memory is used heavily and the performance is greatly impacted.

    So in this case, it would be better not to use group functionality since there are large sets of data in the underlying source. 

    Possibly, if the large set data could be grouped into small parts (I mean the records in a group is relative small, maybe only 100), you can consider using LINQ to group the data and customizing an expander. After a click on the expander, we can load the specified group into view. That is we can load the data on demand.

    For dealing with large sets of items with ItemsControl in WPF, you can refer to this thread on how to fine-tune the performance with data virtualizing. You may especially need to read the PDF version in that thread.

    Feel free to let me know if there’s any problem.

    Best regards,
    Bruce Zhou


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Welcome to the All-In-One Code Framework! If you have any feedback, please tell us.
    • Marked as answer by Bruce.Zhou Thursday, September 03, 2009 9:23 AM
    Wednesday, September 02, 2009 6:42 AM
  • Thank you Mr. Zhou.

    Sincerely,

    JD
    Thursday, September 10, 2009 7:46 PM