using System; using System.ComponentModel; using System.Threading.Tasks; using global::Windows.UI.Xaml; using System.Maui.Internals; namespace System.Maui.Platform.UWP { public class ImageRenderer : ViewRenderer, IImageVisualElementRenderer { bool _measured; bool _disposed; public ImageRenderer() : base() { ImageElementManager.Init(this); global::Windows.UI.Xaml.Application.Current.Resuming += OnResumingAsync; } bool IImageVisualElementRenderer.IsDisposed => _disposed; public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint) { if (Control.Source == null) return new SizeRequest(); _measured = true; return new SizeRequest(Control.Source.GetImageSourceSize()); } protected override void Dispose(bool disposing) { if (_disposed) { return; } _disposed = true; if (disposing) { ImageElementManager.Dispose(this); if (Control != null) { Control.ImageOpened -= OnImageOpened; Control.ImageFailed -= OnImageFailed; global::Windows.UI.Xaml.Application.Current.Resuming -= OnResumingAsync; } } base.Dispose(disposing); } protected override async void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); if (e.NewElement != null) { if (Control == null) { var image = new global::Windows.UI.Xaml.Controls.Image(); image.ImageOpened += OnImageOpened; image.ImageFailed += OnImageFailed; SetNativeControl(image); } await TryUpdateSource().ConfigureAwait(false); } } protected override async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName == Image.SourceProperty.PropertyName) await TryUpdateSource().ConfigureAwait(false); } void OnImageOpened(object sender, RoutedEventArgs routedEventArgs) { if (_measured) { ImageElementManager.RefreshImage(this); } Element?.SetIsLoading(false); } protected virtual void OnImageFailed(object sender, ExceptionRoutedEventArgs exceptionRoutedEventArgs) { Log.Warning("Image Loading", $"Image failed to load: {exceptionRoutedEventArgs.ErrorMessage}"); Element?.SetIsLoading(false); } protected virtual async Task TryUpdateSource() { // By default we'll just catch and log any exceptions thrown by UpdateSource so we don't bring down // the application; a custom renderer can override this method and handle exceptions from // UpdateSource differently if it wants to try { await UpdateSource().ConfigureAwait(false); } catch (Exception ex) { Log.Warning(nameof(ImageRenderer), "Error loading image: {0}", ex); } finally { ((IImageController)Element)?.SetIsLoading(false); } } protected async Task UpdateSource() { await ImageElementManager.UpdateSource(this).ConfigureAwait(false); } async void OnResumingAsync(object sender, object e) { try { await ImageElementManager.UpdateSource(this); } catch (Exception exception) { Log.Warning("Update image source after app resume", $"ImageSource failed to update after app resume: {exception.Message}"); } } void IImageVisualElementRenderer.SetImage(global::Windows.UI.Xaml.Media.ImageSource image) { Control.Source = image; } global::Windows.UI.Xaml.Controls.Image IImageVisualElementRenderer.GetImage() => Control; } }