Why program crashes by adding a lot of images to listview?

后端 未结 2 893
庸人自扰
庸人自扰 2021-01-17 08:44

This is my XAML:

         


        
相关标签:
2条回答
  • 2021-01-17 09:07

    After some long time I found several solutions of this problem. There are easy ways, as for me, and some harder ways. The easiest way, I think: In my code in the question we need to change code below:

    <ItemsPanelTemplate>
        <WrapPanel Orientation="Horizontal" Width="{Binding Path=ActualWidth, ElementName=ImageList}"/>
    </ItemsPanelTemplate>
    

    "WrapPanel" replace on "VirtualizingStackPanel". In this way program will work as user @mkArtak suggested:

    Also, you should use some kind of virtualization when showing the image thumbnails in the bottom area, as you don't want to load all the images at once. Feels like pretty common scenario to have a control for that available somewhere, which you can reuse. The idea is to load only the images which are visible +2 maybe from each side. Then, load anything else, as the user scrolls.

    As I understood this is the virtualization. Of course, you can tune how virtualization must be. You can find info about that in internet.

    And a little more harder way: using async\await what suggested user @Clemens

    You could have a view model that loads thumbnail image files asynchronously, and also limits their size by setting the DecodePixelWidth or DecodePixelHeight property.

    Also, we can use both of this ways and it will be the best way, I think.

    Thanks very much everyone for your help in finding the solution of this problem.

    0 讨论(0)
  • 2021-01-17 09:08

    You could have a view model that loads thumbnail image files asynchronously, and also limits their size by setting the DecodePixelWidth or DecodePixelHeight property.

    public class ImageData
    {
        public string Name { get; set; }
        public ImageSource ImageSource { get; set; }
    }
    
    public class ViewModel
    {
        public ObservableCollection<ImageData> Images { get; }
            = new ObservableCollection<ImageData>();
    
        public async Task LoadFolder(string folderName, string extension = "*.jpg")
        {
            Images.Clear();
    
            foreach (var path in Directory.EnumerateFiles(folderName, extension))
            {
                Images.Add(new ImageData
                {
                    Name = Path.GetFileName(path),
                    ImageSource = await LoadImage(path)
                });
            }
        }
    
        public Task<BitmapImage> LoadImage(string path)
        {
            return Task.Run(() =>
            {
                var bitmap = new BitmapImage();
    
                using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    bitmap.BeginInit();
                    bitmap.DecodePixelHeight = 100;
                    bitmap.CacheOption = BitmapCacheOption.OnLoad;
                    bitmap.StreamSource = stream;
                    bitmap.EndInit();
                    bitmap.Freeze();
                }
    
                return bitmap;
            });
        }
    }
    

    You would bind to such a view model like this:

    <Window.DataContext>
        <local:ViewModel/>
    </Window.DataContext>
    ...
    <ListBox ItemsSource="{Binding Images}"
                ScrollViewer.HorizontalScrollBarVisibility="Disabled">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Grid Width="200" Height="130">
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                        <RowDefinition Height="30"/>
                    </Grid.RowDefinitions>
                    <Image Source="{Binding ImageSource}"/>
                    <TextBlock Grid.Row="1" Text="{Binding Name}"/>
                </Grid>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    And populate it e.g. in some async input event handler, like:

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        await ((ViewModel)DataContext).LoadFolder(...);
    }
    
    0 讨论(0)
提交回复
热议问题