简体   繁体   中英

Out of memory exception while using <ScrollViewer> with multiple ListViews in it

I have a usercontrol with 2 ListViews in it. One for holding a list of predefined categories and one for the list with all the categories in it. When i place the ListViews inside a <Grid> than everything works perfect. The working xaml code (with Grid):

<Grid Style="{StaticResource ResourceKey=ContentStyle}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <ListView x:Name="lstPredefinedCategories" Grid.Row="0" ItemsSource="{Binding PredefinedCategories}" SelectionMode="Multiple" Margin="20">
            <ListView.Header>
                <StackPanel>
                    <TextBlock Text="Voorgestelde categorie&#235;n" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
                    <Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
                </StackPanel>
            </ListView.Header>

            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
        <Grid Grid.Row="1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <StackPanel Grid.Row="0" Margin="20,0">
                <TextBlock Text="Alle categorie&#235;n" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
                <Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
            </StackPanel>
            <TextBox x:Name="txtSearch" PlaceholderText="Zoek categorie" Grid.Row="1" Style="{StaticResource SearchboxStyle}" Margin="20,0" TextChanged="txtSearch_TextChanged" />
            <Rectangle Grid.Row="2" Style="{StaticResource DividerStyle}" Margin="20, 0" />
            <ListView x:Name="lstCategories" Grid.Row="3" Margin="20,10,20,0" ItemsSource="{Binding Categories}" SelectionMode="Multiple" SelectionChanged="lstCategories_SelectionChanged">

                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Key}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Rectangle Grid.Row="4" Style="{StaticResource DividerStyle}" Margin="20, 0" />
            <Grid Grid.Row="5">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Button x:Name="btnAnnuleren" Grid.Column="0" Content="Annuleren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnAnnuleren_Click"/>
                <Rectangle Grid.Column="1" Fill="#A9A9A9" Width="0.5" Margin="10,0" />
                <Button x:Name="btnSelecteren" Grid.Column="2" Content="Selecteren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnSelecteren_Click"/>
            </Grid>
        </Grid>
    </Grid>

The only problem with this is that I dont get the UI behaviour that I want. If I use a grid then only the red border is scrollable (because of the ListView). But what I need is that the entire green border is scrollable.

WP布局

So I want to put everything in a <ScrollViewer><StackPanel></StackPanel></ScrollViewer> . But when I do so, I occasionally get an out-of-memory exception (sometimes the apps just freezes and close without an exception).

Here is my not working xaml with the <ScrollViewer> :

<ScrollViewer>
        <StackPanel>
            <ListView x:Name="lstPredefinedCategories" ItemsSource="{Binding PredefinedCategories}" SelectionMode="Multiple" Margin="20">
                <ListView.Header>
                    <StackPanel>
                        <TextBlock Text="Voorgestelde categorie&#235;n" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
                        <Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
                    </StackPanel>
                </ListView.Header>

                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <StackPanel Grid.Row="0" Margin="20,0">
                    <TextBlock Text="Alle categorie&#235;n" Style="{StaticResource TextBlockStyle}" FontWeight="SemiBold" Foreground="Black" />
                    <Rectangle Style="{StaticResource DividerStyle}" Fill="Black"/>
                </StackPanel>
                <TextBox x:Name="txtSearch" PlaceholderText="Zoek categorie" Grid.Row="1" Style="{StaticResource SearchboxStyle}" Margin="20,0" TextChanged="txtSearch_TextChanged" />
                <Rectangle Grid.Row="2" Style="{StaticResource DividerStyle}" Margin="20, 0" />
                <ListView x:Name="lstCategories" Grid.Row="3" Margin="20,10,20,0" ItemsSource="{Binding Categories}" SelectionMode="Multiple" SelectionChanged="lstCategories_SelectionChanged">

                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Key}" Style="{StaticResource TextBlockStyle}" HorizontalAlignment="Left" TextWrapping="Wrap" Width="300" />
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
                <Rectangle Grid.Row="4" Style="{StaticResource DividerStyle}" Margin="20, 0" />
                <Grid Grid.Row="5">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Button x:Name="btnAnnuleren" Grid.Column="0" Content="Annuleren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnAnnuleren_Click"/>
                    <Rectangle Grid.Column="1" Fill="#A9A9A9" Width="0.5" Margin="10,0" />
                    <Button x:Name="btnSelecteren" Grid.Column="2" Content="Selecteren" Style="{StaticResource ButtonAnnulerenStyle}" Click="btnSelecteren_Click"/>
                </Grid>
            </Grid>
        </StackPanel>
    </ScrollViewer>

Any thoughts on why my app is freezing or get an OOM-exception?

Update

It comes because in the 2nd ListView they are too much objects loaded. So I'm gonna try to fix it with ISupportIncrementalLoading.

Or is there an other way?

The solution was to use virtualization (ISupportIncrementalLoading) like suggested in the comments.

Here you can find my implementation class of ISupportIncrementalLoading:

public class StringKeyValueIncrementalCollection : ObservableCollection<StringKeyValue>, ISupportIncrementalLoading
    {

        private List<StringKeyValue> allCategories;
        private int lastItem = 1;

        public StringKeyValueIncrementalCollection(List<StringKeyValue> categories)
        {
            this.allCategories = categories;
        }

        public bool HasMoreItems
        {
            get
            {
                if (lastItem == allCategories.Count)
                {
                    return false;
                }
                else
                {
                    return true;
                }
            }
        }

        public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
        {

            CoreDispatcher coreDispatcher = Window.Current.Dispatcher;

            return Task.Run<LoadMoreItemsResult>(async () =>
            {

                List<StringKeyValue> items = new List<StringKeyValue>();
                for (int i = 0; i < count; i++)
                {
                    items.Add(allCategories[i]);
                    lastItem++;
                    Debug.WriteLine(lastItem);
                    if (lastItem == allCategories.Count)
                    {
                        break;
                    }
                }

                await coreDispatcher.RunAsync(CoreDispatcherPriority.Normal,
                    () =>
                    {
                        foreach (StringKeyValue item in items)
                        {
                            this.Add(item);
                        }
                    });

                return new LoadMoreItemsResult() { Count = count };
            }).AsAsyncOperation<LoadMoreItemsResult>();
        }
    }

And then my code in the ViewModel. As you can see, I use the StringKeyValueIncrementalCollection instead of a regular List<object> :

    private StringKeyValueIncrementalCollection categories;
    private StringKeyValueIncrementalCollection allCategories;

    public StringKeyValueIncrementalCollection Categories
    {
        get { return categories; }
        set
        {
            filteredCategories = value;
            RaisePropertyChanged("Categories");
        }
    }
    public async void LoadCategories()
    {
        List<StringKeyValue> temp = await this.openVlaanderenService.GetCategoriesData();
        allCategories = new StringKeyValueIncrementalCollection(temp);
        Categories = allCategories;
    }

The only problem that you than have is that the ScollViewer will allow it's content to fill as much space as it wants, so the data just will keep loading. To fix that I did what was suggested in ISupportIncrementalLoading inside ScrollViewer not supported?

So I added a SizeChanged="ScrollViewer_SizeChanged" event to my ScrollViewer and in code behind set the size of the ListView based on the viewport size properties of the ScrollViewer:

private void ScrollViewer_SizeChanged(object sender, SizeChangedEventArgs e)
{
    lstCategories.Height = scrollViewer.ViewportHeight;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM