1

I have a huge amount of pictures that need to be loaded in my wpf app. I tried it with a BackgroundWorker but it cannot create the togglebutton where the image is show on.

Is there a better way to load a huge amount of images? They need to be selectable because the user can choose an image.

Here's a bit of my code so far:

<WrapPanel Name="mFolderImages">
    <ToggleButton Width="150" Margin="5" Style="{StaticResource ImageList}">
         <ToggleButton.Content>
              <Image Source="/Managment;component/images/example.png" />
         </ToggleButton.Content>
    </ToggleButton>
</WrapPanel>

private void GetFolderImagesThreadFinished(object sender, RunWorkerCompletedEventArgs e) {
        if (e.Result != null && e.Result is List<BitmapImage>) {
            List<BitmapImage> images = (List<BitmapImage>)e.Result;
            foreach (var image in images) {
                Image img = new Image();
                img.Source = image;
                img.Margin = new Thickness(5);

                ToggleButton btn = new ToggleButton();
                btn.Content = img;
                btn.Width = 150;
                btn.Margin = new Thickness(5);
                btn.IsEnabled = true;
                btn.Click += ChangeSelectedImage;
                btn.Style = this.FindResource("ImageList") as Style;
                mFolderImages.Children.Add(btn);
            }
        }
        mProgress.Visibility = Visibility.Collapsed;
        mFolderImages.IsEnabled = true;
    }

    private void GetFolderImagesThread(object sender, DoWorkEventArgs e) {
        string imagePath = Config.GetValue("ImagePath");
        if (!Directory.Exists(imagePath)) return;

        string[] files = Directory.GetFiles(imagePath);
        int progress = 0;
        List<BitmapImage> images = new List<BitmapImage>();

        foreach(var file in files) {
            if (file.EndsWith(".jpg") || file.EndsWith(".png")) {
                try {
                    BitmapImage bmp = new BitmapImage();
                    bmp.BeginInit();
                    bmp.UriSource = new Uri(file, UriKind.Absolute);
                    bmp.EndInit();
                    bmp.Freeze();
                    images.Add(bmp);
                } catch (Exception ex) {
                    Console.Write(ex.Message);
                }
            }
            ++progress;
            mThread.ReportProgress((int)((progress / (float)files.Length) * 100));
        }
        e.Result = images;
    }
Calypoter
  • 155
  • 2
  • 11
  • possible duplicate of [Loading image in thread with WPF](http://stackoverflow.com/questions/1738978/loading-image-in-thread-with-wpf) – Vitor M. Barbosa Jul 21 '15 at 16:49
  • Hard to tell without having seen any of your code. – Clemens Jul 21 '15 at 16:55
  • @Clemens Added some code – Calypoter Jul 21 '15 at 17:49
  • 1
    @VitorM.Barbosa That is not the problem, it's showing them that blocks the ui. – Calypoter Jul 21 '15 at 17:50
  • You should use a ListBox instead, and bind its ItemsSource property to a collection of image file paths. In the ItemTemplate of the ListBox, use an Image control and bind its Source property to the file path, like ``. All the loading would already be done asynchronously by the WPF BitmapFrame class, which is used internally by the ImageSourceConverter that automatically converts string to ImageSource. This approach would also benefit from virtualization, i.e. ListBox items wouldn't be loaded until they get scrolled into view. – Clemens Jul 21 '15 at 17:53

2 Answers2

1

Create a list box for all the image, create an obersvable collection for the images. I wouldn't use the image but would create a thumbnail with lower resolution for the image.
Each viewModel of an image will be shown using a datatemplate for the view model which will display the thumbnail.
Also have a quick look here for async loading of the images Loading Images asynchronous in C#

<ListBox ItemsSource="{Binding ImagesCollection}" 
   SelectedIndex="0">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Image Source="{Binding}"/> <!--bind to the field of the tumbnail using a converter if needed
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>
Community
  • 1
  • 1
Gilad
  • 6,437
  • 14
  • 61
  • 119
0

Ok, I got it.

Used the Listbox in xaml with a Wrappanel in ItemsPanel and the image in ItemTemplate.

Then in code I use the BackgroundWorker to load and resize the image and use the reportProgress to add it to the collection.

<ListBox ItemsSource="{Binding Path=ImagesCollection, IsAsync=True}" SelectedIndex="0" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
   <ListBox.ItemsPanel>
      <ItemsPanelTemplate>
           <WrapPanel IsItemsHost="True" />
      </ItemsPanelTemplate>
   </ListBox.ItemsPanel>
   <ListBox.ItemTemplate>
       <DataTemplate>
          <Image Width="150" Height="150"  Source="{Binding}" />
       </DataTemplate>
   </ListBox.ItemTemplate>
</ListBox>

private void LoadImageProgress(object sender, ProgressChangedEventArgs e) {
        if (e.UserState != null && e.UserState is BitmapImage) {
            ImagesCollection.Add((BitmapImage)e.UserState);
        }
    }

    private void LoadImageThread(object sender, DoWorkEventArgs e) {
        string imagePath = Config.GetValue("ImagePath");
        if (!Directory.Exists(imagePath)) return;

        string[] files = Directory.GetFiles(imagePath);
        foreach (var file in files) {
            if (file.EndsWith(".jpg") || file.EndsWith(".png")) {
                var img = new Bitmap(file);
                float scale = Math.Min(150 / (float)img.Width, 150 / (float)img.Height);

                var bmp = new Bitmap(150, 150);
                var graph = Graphics.FromImage(bmp);

                var scaleWidth = (int)(img.Width * scale);
                var scaleHeight = (int)(img.Height * scale);

                graph.DrawImage(img, new System.Drawing.Rectangle((150 - scaleWidth) / 2, (150 - scaleHeight) / 2, scaleWidth, scaleHeight));

                BitmapImage retImg = new BitmapImage();
                using (MemoryStream mem = new MemoryStream()) {
                    bmp.Save(mem, System.Drawing.Imaging.ImageFormat.Bmp);
                    mem.Position = 0;
                    retImg.BeginInit();
                    retImg.StreamSource = mem;
                    retImg.CacheOption = BitmapCacheOption.OnLoad;
                    retImg.EndInit();
                    retImg.Freeze();
                }
                mThread.ReportProgress(0, retImg);

                if (mThread.CancellationPending) return;
                //Thread.Sleep(1000);
            }
        }
    }
Calypoter
  • 155
  • 2
  • 11