Reputation: 147
In WPF, I'm trying to load many (thousands) of images into a ListBox WrapPanel.
I'm attempting to load the images similar to how a Windows Explorer window does.
So far I have my code which loads all the images' name, location (as tag), and a placeholder image to speed up load time:
Dim ofd As New Microsoft.Win32.OpenFileDialog()
ofd.Multiselect = True
ofd.Filter = "JPEG|*.jpg"
If ofd.ShowDialog() Then
For Each f In ofd.FileNames
Items.Add(New With {.img = New BitmapImage(New Uri("pack://application:,,,/Resources/PlaceholderPhoto.png")), .nam = Path.GetFileNameWithoutExtension(f), .tag = f})
Next
'The name of my ObservableCollection is Items'
lstboxPhotos.ItemsSource = Items
End If
That part is fine. What I'm trying to do after is load the images' thumbnails dynamically (one-by-one). I'm doing this so the user can interact with and see how many images are available while the thumbnails are loading. I've tried a couple different things - a background worker, dispatcher, and separate thread.
The code I'm using to load the images is below:
'i came from me doing a for..next for each image in Items collection'
Dim bmp As New BitmapImage()
bmp.BeginInit()
bmp.DecodePixelWidth = 90
bmp.DecodePixelHeight = 60
bmp.CacheOption = BitmapCacheOption.OnLoad
bmp.UriSource = New Uri(Items.Item(i).tag, UriKind.Absolute)
bmp.EndInit()
Items.Item(i).img = bmp
I've search all over the internet. I'm honestly not sure what direction to take in order to do what I'm needing to.
Let me know if I need to clarify anything else. Thank you in advance! :)
Edit: Okay so referring to this article, using the 2nd answer I got the items to load one at a time. It loads all of the items' names just fine, but seems to stop loading the images after about 40 items.
If anyone could shed light on why it might cease loading the thumbnails, but continue loading the items' names that would be a great help! Thanks!
Upvotes: 3
Views: 1420
Reputation: 147
Okay I know it's been a while, but I wanted to post what I ended up doing.
First, I loaded all the image names into the ListBox normally using an ObservableCollection
with a temporary image:
Dim Items As New ObservableCollection(Of Object)
Dim Files() As String
...
For Each f In Files
Items.Add(New With {.img = New BitmapImage(New Uri("/Resources/Photo.png")), .name = f})
Next
lbPhotos.ItemsSource = Items
Then I used a BackgroundWorker
to replace each placeholder image with the actual image:
Private WithEvents bw As New BackgroundWorker
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles bw.DoWork
...
For i = 0 To Items.Count - 1
If bw.CancellationPending Then
e.Cancel = True
Else
Dim n As String = Items(i).name
Dim t As String = Items(i).tag
Dim bmp As New BitmapImage
bmp.BeginInit()
bmp.UriSource = New Uri(PathToImage, UriKind.Absolute)
bmp.EndInit()
bmp.Freeze()
Dispatcher.BeginInvoke(Sub()
Items.RemoveAt(i)
Items.Insert(i, New With {.img = bmp, .name = n})
End Sub)
End If
Next
End Sub
That allows for the user to interact with the UI while the images load.
Upvotes: 1
Reputation: 69989
You can do that quickly and easily using the built in TypeConverter
that converts string
file paths into ImageSource
objects. Take this simple example that will show thumbnails of all of the images in your Pictures
folder:
public ObservableCollection<string> FilePaths { get; set; }
...
FilePaths = new ObservableCollection<string>(Directory.GetFiles(
Environment.GetFolderPath(Environment.SpecialFolder.MyPictures)));
...
<ItemsControl ItemsSource="{Binding FilePaths}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}" Width="100" Stretch="Uniform" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
In this example, each Image.Source
property is data bound directly with one of the file paths in the FilePaths
collection.
Upvotes: 4