/// <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(); }