| File: ImageElement.cs | Web Access |
| Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls) |
#nullable disable using System; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Graphics; namespace Microsoft.Maui.Controls { static class ImageElement { /// <summary>Bindable property for <c>ImageSource</c>.</summary> public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create("ImageSource", typeof(ImageSource), typeof(IImageElement), default(ImageSource), propertyChanging: OnImageSourceChanging, propertyChanged: OnImageSourceChanged); /// <summary>Bindable property for <see cref="IImageElement.Source"/>.</summary> public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(IImageElement.Source), typeof(ImageSource), typeof(IImageElement), default(ImageSource), propertyChanging: OnImageSourceChanging, propertyChanged: OnImageSourceChanged); /// <summary>Bindable property for <see cref="IImageElement.Aspect"/>.</summary> public static readonly BindableProperty AspectProperty = BindableProperty.Create(nameof(IImageElement.Aspect), typeof(Aspect), typeof(IImageElement), Aspect.AspectFit); /// <summary>Bindable property for <see cref="IImageElement.IsOpaque"/>.</summary> public static readonly BindableProperty IsOpaqueProperty = BindableProperty.Create(nameof(IImageElement.IsOpaque), typeof(bool), typeof(IImageElement), false); internal static readonly BindableProperty IsAnimationPlayingProperty = BindableProperty.Create(nameof(IImageElement.IsAnimationPlaying), typeof(bool), typeof(IImageElement), false); static void OnImageSourceChanged(BindableObject bindable, object oldValue, object newValue) { var newSource = (ImageSource)newValue; var image = (IImageElement)bindable; if (newSource != null && image != null) newSource.SourceChanged += image.OnImageSourceSourceChanged; ImageSourceChanged(bindable, newSource); } static void OnImageSourceChanging(BindableObject bindable, object oldValue, object newValue) { var oldSource = (ImageSource)oldValue; var image = (IImageElement)bindable; if (oldSource != null && image != null) oldSource.SourceChanged -= image.OnImageSourceSourceChanged; ImageSourceChanging(oldSource); } #pragma warning disable CS0618 // Type or member is obsolete public static SizeRequest Measure(IImageElement ImageElementManager, SizeRequest desiredSize, double widthConstraint, double heightConstraint) { double desiredAspect = desiredSize.Request.Width / desiredSize.Request.Height; double constraintAspect = widthConstraint / heightConstraint; double desiredWidth = desiredSize.Request.Width; double desiredHeight = desiredSize.Request.Height; if (desiredWidth == 0 || desiredHeight == 0) return new SizeRequest(new Size(0, 0)); double width = desiredWidth; double height = desiredHeight; if (constraintAspect > desiredAspect) { // constraint area is proportionally wider than image switch (ImageElementManager.Aspect) { case Aspect.AspectFit: case Aspect.AspectFill: height = Math.Min(desiredHeight, heightConstraint); width = desiredWidth * (height / desiredHeight); break; case Aspect.Fill: width = Math.Min(desiredWidth, widthConstraint); height = desiredHeight * (width / desiredWidth); break; } } else if (constraintAspect < desiredAspect) { // constraint area is proportionally taller than image switch (ImageElementManager.Aspect) { case Aspect.AspectFit: case Aspect.AspectFill: width = Math.Min(desiredWidth, widthConstraint); height = desiredHeight * (width / desiredWidth); break; case Aspect.Fill: height = Math.Min(desiredHeight, heightConstraint); width = desiredWidth * (height / desiredHeight); break; } } else { // constraint area is same aspect as image width = Math.Min(desiredWidth, widthConstraint); height = desiredHeight * (width / desiredWidth); } return new SizeRequest(new Size(width, height)); } #pragma warning restore CS0618 // Type or member is obsolete public static void OnBindingContextChanged(IImageElement image, VisualElement visualElement) { if (image.Source != null) BindableObject.SetInheritedBindingContext(image.Source, visualElement?.BindingContext); } public static void ImageSourceChanging(ImageSource oldImageSource) { if (oldImageSource == null) return; try { // This method generates are particularly unhappy async/await state-machine combined // with the CoW mechanisms of the try/catch that actually makes it worth avoiding // if oldValue is null (which it often is). Early return does not prevent the // state-machine mechanisms being kicked in, only moving to another method will do that. CancelOldValue(oldImageSource); } catch (ObjectDisposedException) { // Workaround bugzilla 37792 https://bugzilla.xamarin.com/show_bug.cgi?id=37792 } } async static void CancelOldValue(ImageSource oldvalue) { try { await oldvalue.Cancel(); } catch (ObjectDisposedException) { // Workaround bugzilla 37792 https://bugzilla.xamarin.com/show_bug.cgi?id=37792 } } static void ImageSourceChanged(BindableObject bindable, ImageSource newSource) { var imageElement = (VisualElement)bindable; if (newSource != null) newSource.Parent = imageElement; imageElement?.InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged); } public static void ImageSourceSourceChanged(object sender, EventArgs e) { if (sender is IImageElement imageController) imageController.RaiseImageSourcePropertyChanged(); ((VisualElement)sender).InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged); } internal static bool GetLoadAsAnimation(BindableObject bindable) { return bindable.IsSet(IsAnimationPlayingProperty); } } }