Example #1
0
        /// <summary>
        /// Disposes of this instance.
        /// </summary>
        public void Dispose()
        {
            if (!_isDisposed)
            {
                _stream?.Dispose();
                _packageReader?.Dispose();

                GC.SuppressFinalize(this);

                _isDisposed = true;
            }
        }
        /// <summary>
        /// Converts IconUrl from PackageItemListViewModel to an image represented by a BitmapSource
        /// </summary>
        /// <param name="values">An array of two elements containing the IconUri and a generator function of PackageReaderBase objects</param>
        /// <param name="targetType">unused</param>
        /// <param name="parameter">A BitmapImage that will be used as the default package icon</param>
        /// <param name="culture">unused</param>
        /// <returns>A BitmapSource with the image</returns>
        /// <remarks>
        /// We bind to a BitmapImage instead of a Uri so that we can control the decode size, since we are displaying 32x32 images, while many of the images are 128x128 or larger.
        /// This leads to a memory savings.
        /// </remarks>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values == null || values.Length == 0)
            {
                return(null);
            }

            var iconUrl            = values[0] as Uri;
            var defaultPackageIcon = parameter as BitmapSource;

            if (iconUrl == null)
            {
                return(null);
            }

            string cacheKey          = GenerateKeyFromIconUri(iconUrl);
            var    cachedBitmapImage = BitmapImageCache.Get(cacheKey) as BitmapSource;

            if (cachedBitmapImage != null)
            {
                return(cachedBitmapImage);
            }

            // Some people run on networks with internal NuGet feeds, but no access to the package images on the internet.
            // This is meant to detect that kind of case, and stop spamming the network, so the app remains responsive.
            if (ErrorFloodGate.IsOpen)
            {
                return(defaultPackageIcon);
            }

            var iconBitmapImage = new BitmapImage();

            iconBitmapImage.BeginInit();

            BitmapSource imageResult;

            if (IsEmbeddedIconUri(iconUrl))
            {
                // Check if we have enough info to read the icon from the package
                if (values.Length == 2 && values[1] is Func <PackageReaderBase> lazyReader)
                {
                    try
                    {
                        PackageReaderBase reader = lazyReader(); // Always returns a new reader. That avoids using an already disposed one
                        if (reader is PackageArchiveReader par)  // This reader is closed in BitmapImage events
                        {
                            var iconEntryNormalized = PathUtility.StripLeadingDirectorySeparators(
                                Uri.UnescapeDataString(iconUrl.Fragment)
                                .Substring(1));     // Substring skips the '#' in the URI fragment
                            iconBitmapImage.StreamSource = par.GetEntry(iconEntryNormalized).Open();

                            void EmbeddedIcon_DownloadOrDecodeFailed(object sender, ExceptionEventArgs args)
                            {
                                reader.Dispose();
                                CheckForFailedCacheEntry(cacheKey, args);
                            }

                            void EmbeddedIcon_DownloadCompleted(object sender, EventArgs args)
                            {
                                reader.Dispose();
                                IconBitmapImage_DownloadCompleted(sender, args);
                            }

                            iconBitmapImage.DecodeFailed      += EmbeddedIcon_DownloadOrDecodeFailed;
                            iconBitmapImage.DownloadFailed    += EmbeddedIcon_DownloadOrDecodeFailed;
                            iconBitmapImage.DownloadCompleted += EmbeddedIcon_DownloadCompleted;

                            imageResult = FinishImageProcessing(iconBitmapImage, iconUrl, defaultPackageIcon);
                        }
                        else // we cannot use the reader object
                        {
                            reader?.Dispose();
                            AddToCache(cacheKey, defaultPackageIcon);
                            imageResult = defaultPackageIcon;
                        }
                    }
                    catch (Exception)
                    {
                        AddToCache(cacheKey, defaultPackageIcon);
                        imageResult = defaultPackageIcon;
                    }
                }
                else // Identified an embedded icon URI, but we are unable to process it
                {
                    AddToCache(cacheKey, defaultPackageIcon);
                    imageResult = defaultPackageIcon;
                }
            }
            else
            {
                iconBitmapImage.UriSource = iconUrl;

                iconBitmapImage.DecodeFailed      += IconBitmapImage_DownloadOrDecodeFailed;
                iconBitmapImage.DownloadFailed    += IconBitmapImage_DownloadOrDecodeFailed;
                iconBitmapImage.DownloadCompleted += IconBitmapImage_DownloadCompleted;

                imageResult = FinishImageProcessing(iconBitmapImage, iconUrl, defaultPackageIcon);
            }

            return(imageResult);
        }
 public void Dispose()
 {
     _stream?.Dispose();
     _packageReader?.Dispose();
 }