0

I have a listview with Image control, bound with Uri value (image path on disk). I want to convert only those Uri in "Field of view" and dispose (or convert back to Uri) which goes out of "Field of View".

What I have tried:

I have used IValueConverter to convert Uri to Image but ConvertBack is not firing.

<Image Source="{Binding PageSource,Mode=TwoWay, UpdateSourceTrigger=Explicit, Converter={StaticResource ImageConverter}}" HorizontalAlignment="Center" VerticalAlignment="Top"/>

Source code:

    private void LoadPdfFile(string fileName)
    {
        string folderPath = Environment.CurrentDirectory + "//ImageCache";
        Directory.CreateDirectory(folderPath);

        //concurrent queue- thread safe
        ConcurrentQueue<PdfPage> cQueue = new ConcurrentQueue<PdfPage>();

        //loading pdf file
        using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
        {
            //syncfusion pdf library to load pdf file
            using (PdfLoadedDocument ldoc = new PdfLoadedDocument(fs))
            {
                //parallel for- to simultaneous loop
                Parallel.For(0, ldoc.Pages.Count,
                         i =>
                         {
                             //convert pdf page into bitmap
                             Bitmap img = ldoc.ExportAsImage(i);

                             //saving image onto disk
                             //I don't want to load it into memory directly to save memory(but it is not working)
                             string imageName = folderPath + "//" + Guid.NewGuid().ToString();
                             img.Save(imageName);

                             //filling object value
                             PdfPage newFile = new PdfPage();
                             //Uri PageSource
                             newFile.PageSource = new Uri(imageName, UriKind.Absolute);
                             //int PageNumber
                             newFile.PageNumber = i + 1;
                             cQueue.Enqueue(newFile);
                             //disposing bitmap value
                             img.Dispose();
                             img = null;
                             GC.Collect();
                             GC.WaitForPendingFinalizers();
                         });
            }
        }

        List<PdfPage> Pages = cQueue.OrderBy(x => x.PageNumber).ToList();
        //assigning it to the collectionviewsource
        itemCollectionViewSource.Source = Pages;
    }
    public class ImageConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
            {
                return null;
            }
            else
            {
                var uri = (Uri)value;
                BitmapImage bmp = new BitmapImage();
                bmp.BeginInit();
                bmp.UriSource = uri;
                bmp.UriCachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
                bmp.CacheOption = BitmapCacheOption.None;
                bmp.EndInit();
                return bmp;
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }

Update: I have changed the code to

Parallel.For(0, ldoc.Pages.Count,
                             i =>
                             {
                                 using (Bitmap img = ldoc.ExportAsImage(i))
                                 {
                                     using (Stream stream = new MemoryStream())
                                     {
                                         PdfPage newFile = new PdfPage();

                                         img.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp);

                                         BitmapImage bmp = new BitmapImage();
                                         bmp.BeginInit();
                                         bmp.CacheOption = BitmapCacheOption.OnLoad;
                                         bmp.StreamSource = stream;
                                         bmp.EndInit();
                                         bmp.Freeze();
                                         //BitmapImage PageSource
                                         newFile.PageSource = bmp;

                                         newFile.PageNumber = i + 1;
                                         cQueue.Enqueue(newFile);
                                     }
                                 }
                                 GC.Collect();
                                 GC.WaitForPendingFinalizers();
                             });    

but this throws A generic error occurred in GDI+ error for large pages pdf file.

Pravin Chopade
  • 77
  • 1
  • 14
  • 1
    There is nothing to convert back. The Source property Binding is effectively OneWay (even if you declare it TwoWay). Your Converter creates an ImageSource that is passed to the Image element which uses it as long as necessary. Then the garbage collector removes it. You may however tweak the creation process of the ImageSource to avoid unnecessary caching etc. E.g. like this: https://stackoverflow.com/a/26532667/1136211 – Clemens Aug 16 '18 at 07:31
  • 1
    You don't "release memory of image controls" explicitly in a .NET application. The gargabe collector does this for you eventually. – mm8 Aug 16 '18 at 09:04
  • @Clemens I have already used same for Converting uri to imagesource; but as I have a large number of images, application memory size keeps increasing and it crashes afterwards by out of memory exception. – Pravin Chopade Aug 18 '18 at 04:47
  • @mm8 I am already using GC.Collect() and GC.WaitForPendingFinalizers() – Pravin Chopade Aug 18 '18 at 04:50
  • That's fine. However, we could not have know it, because you didn't show us the relevant parts of your code. In case you really need help with this, you'd have to be a lot more specific. – Clemens Aug 18 '18 at 06:33
  • @Clemens sorry for not posting code earlier; I have updated question now. – Pravin Chopade Aug 18 '18 at 19:03
  • It doesn't seem to make sense to save and load image files here. You could directly convert the Bitmap returned from ExportAsImage to a WPF BitmapImage (e.g by encoding to and decoding from a MemoryStream). Make sure to set BitmapCacheOption.OnLoad and dispose of the stream right after EndInit. Also Freeze the BitmapImages. You don't need the Binding Converter. It is also pointless to set UpdateSourceTrigger on a OneWay Binding. – Clemens Aug 18 '18 at 19:28
  • @Clemens I have updated the code with bitmapimage but now facing out memory/GDI++ exception – Pravin Chopade Aug 20 '18 at 02:14
  • Not sure if that's your actual problem, but you should rewind the MemoryStream before loading the BitmapImage: `stream.Position = 0;` – Clemens Aug 20 '18 at 07:02
  • @Clemens I am fetching bitmap with low dpi value **using (Bitmap img = ldoc.ExportAsImage(i, 50F, 50F))** this will keep application memory low. but not able to resolve actual cause. – Pravin Chopade Aug 20 '18 at 08:03

0 Answers0