5

I have a listview which uses the following code:

<ListView x:Name="Display" ItemsSource="{Binding}" Background="#373737" Margin="0,0,350,0" BorderThickness="0" >
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal" Width="767" Height="88">
                    <Border Height="64" Width="64" Margin="12,12,0,12">
                        <Image Source="{Binding Path=album.albumart}" Stretch="UniformToFill"/>
                    </Border>
                    <StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="0,10,0,0">
                        <TextBlock Text="{Binding Path=name}" 
                   Margin="10,0,0,0" Width="300" Height="40" 
                   TextTrimming="WordEllipsis" TextWrapping="Wrap" FontSize="16" HorizontalAlignment="Left"/>
                        <TextBlock Text="{Binding Path=album.name}" 
                   Margin="10,-15,0,0" Width="300" Height="20" 
                   TextTrimming="WordEllipsis" HorizontalAlignment="Left" 
                   FontSize="14" Opacity="0.49"/>
                        <TextBlock Text="{Binding Path=artistname}" 
                   Margin="10,2,0,0" Width="300"
                   TextTrimming="WordEllipsis" HorizontalAlignment="Left" 
                   FontSize="12" Opacity="0.49"/>
                    </StackPanel>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

And I have about 400 objects with images (this takes quite a bit of memory)

Which are then displayed in each listviewitem.

Is there any way for the listview to tell items to load their image from a cache I have based on which objects are visible in the listview instead of having all the images loaded all the time, which, as previously said takes quite a bit of memory.

Hope you guys understand what I'm on about, thank you.

Transcendent
  • 5,598
  • 4
  • 24
  • 47
Tokfrans
  • 1,939
  • 2
  • 24
  • 56
  • 1
    Unfortunately, you can't do this with the default ListView as-is. You could maybe accomplish this with JavaScript. – xspydr Jan 23 '14 at 20:59
  • @Jason: Maybe my knowledge is a little outdated :D but does javascript really work in a C# WPF app ? – Transcendent Jan 23 '14 at 21:03
  • @Jason That really sucks... Guess the only option is to either make the pictures smaller or delete them all. Thanks though – Tokfrans Jan 23 '14 at 21:06
  • Whoops! My bad! You still can't accomplish this with the out of the box WPF ListView. Might have to look into something like RadControls (http://www.telerik.com/products/wpf/overview.aspx). – xspydr Jan 23 '14 at 21:06
  • @Tokfrans, what is the type of `album.albumart`? Did you try to use [`Lazy`](http://msdn.microsoft.com/en-us/library/dd642331(v=vs.110).aspx). `ListView` by default uses virtualization which means it won't render items that aren't shown – dkozl Jan 23 '14 at 21:10
  • @dkozl It's a System.Windows.Media.Imaging.BitmapImage (Think I got it right) But rendering isn't really the problem. Just loading all the items and filling each one with an image takes up quite a lot of memory. – Tokfrans Jan 23 '14 at 21:15
  • @Tokfrans so you can try with `Lazy` to load them only when they are needed and also, if your images are big and you don't need full resolution, you can use `BitmapImage.DecodePixelWidth` and/or `BitmapImage.DecodePixelHeight` to say to what resolution they will be decoded in memory to save space – dkozl Jan 23 '14 at 21:22
  • @dkozl Yeah I will read up on Lazy, thank you. The DecodePixelHeight I've already been using. The thing is that I really want quite a big preview of the images (64x64). – Tokfrans Jan 23 '14 at 21:25
  • @Jason I'm sorry, you have no idea what you're talking about. – Federico Berasategui Jan 23 '14 at 21:59

1 Answers1

4

I tried this solution with my pictures folder containing more than 3500 pictures in high resolution. Memory usage peaked at 120MB with furious scrolling which seemed to trigger garbage collection and reduced memory to about 50MB.

 <ListBox ItemsSource="{Binding Images}" VirtualizingPanel.IsVirtualizing="True">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <Image Height="64" Width="64">
                    <Image.Source>
                        <BitmapImage
                            DecodePixelHeight="64"
                            DecodePixelWidth="64"
                            UriSource="{Binding Path=., Mode=OneWay,UpdateSourceTrigger=Explicit}" 
                            CreateOptions="DelayCreation" 
                            CacheOption="None"  />
                    </Image.Source>
                </Image>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

ViewModel:

public class ViewModel : INotifyPropertyChanged
    {
        public ICollectionView Images { get; private set; }
        public ViewModel()
        {
        }
        public void LoadImages()
        {
            var folder = @"C:\Users\lrved_000\Pictures";
            var photos = System.IO.Directory.EnumerateFiles(folder, "*.jpg",SearchOption.AllDirectories);

            Images = CollectionViewSource.GetDefaultView(photos);
            RaisePropertyChanged("Images");
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void RaisePropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
    }
Lorentz Vedeler
  • 5,101
  • 2
  • 29
  • 40
  • Gives me InvalidOperationException, always complaining that the UriSource or StreamSource should be set (although you are obviously providing a UriSource). VS2022, .Net 6. – J S Jan 12 '22 at 12:04