File: ImageSource.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
#nullable disable
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
 
namespace Microsoft.Maui.Controls
{
	/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="Type[@FullName='Microsoft.Maui.Controls.ImageSource']/Docs/*" />
	[System.ComponentModel.TypeConverter(typeof(ImageSourceConverter))]
	public abstract partial class ImageSource : Element
	{
		readonly object _synchandle = new object();
		CancellationTokenSource _cancellationTokenSource;
 
		TaskCompletionSource<bool> _completionSource;
 
		readonly WeakEventManager _weakEventManager = new WeakEventManager();
 
		internal readonly MergedStyle _mergedStyle;
 
		protected ImageSource()
		{
			_mergedStyle = new MergedStyle(GetType(), this);
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='IsEmpty']/Docs/*" />
		public virtual bool IsEmpty => false;
 
		public static bool IsNullOrEmpty(ImageSource imageSource) =>
			imageSource == null || imageSource.IsEmpty;
 
		private protected CancellationTokenSource CancellationTokenSource
		{
			get { return _cancellationTokenSource; }
			private set
			{
				if (_cancellationTokenSource == value)
					return;
				if (_cancellationTokenSource != null)
					_cancellationTokenSource.Cancel();
				_cancellationTokenSource = value;
			}
		}
 
		bool IsLoading
		{
			get { return _cancellationTokenSource != null; }
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='Cancel']/Docs/*" />
		public virtual Task<bool> Cancel()
		{
			if (!IsLoading)
				return Task.FromResult(false);
 
			TaskCompletionSource<bool> original = Interlocked.CompareExchange(ref _completionSource, new TaskCompletionSource<bool>(), null);
			if (original is null)
			{
				_cancellationTokenSource.Cancel();
				return Task.FromResult(false);
			}
 
			return original.Task;
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='FromFile']/Docs/*" />
		public static ImageSource FromFile(string file)
		{
			return new FileImageSource { File = file };
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='FromResource'][2]/Docs/*" />
		public static ImageSource FromResource(string resource, Type resolvingType)
		{
			return FromResource(resource, resolvingType.Assembly);
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='FromResource'][1]/Docs/*" />
		public static ImageSource FromResource(string resource, Assembly sourceAssembly = null)
		{
			sourceAssembly = sourceAssembly ?? Assembly.GetCallingAssembly();
 
			return FromStream(() => sourceAssembly.GetManifestResourceStream(resource));
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='FromStream'][1]/Docs/*" />
		public static ImageSource FromStream(Func<Stream> stream)
		{
			return new StreamImageSource { Stream = token => Task.Run(stream, token) };
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='FromStream'][2]/Docs/*" />
		public static ImageSource FromStream(Func<CancellationToken, Task<Stream>> stream)
		{
			return new StreamImageSource { Stream = stream };
		}
 
		/// <include file="../../docs/Microsoft.Maui.Controls/ImageSource.xml" path="//Member[@MemberName='FromUri']/Docs/*" />
		public static ImageSource FromUri(Uri uri)
		{
			if (!uri.IsAbsoluteUri)
				throw new ArgumentException("uri is relative");
			return new UriImageSource { Uri = uri };
		}
 
		public static implicit operator ImageSource(string source)
		{
			Uri uri;
			return Uri.TryCreate(source, UriKind.Absolute, out uri) && uri.Scheme != "file" ? FromUri(uri) : FromFile(source);
		}
 
		public static implicit operator ImageSource(Uri uri)
		{
			if (uri == null)
				return null;
 
			if (!uri.IsAbsoluteUri)
				throw new ArgumentException("uri is relative");
			return FromUri(uri);
		}
 
		private protected void OnLoadingCompleted(bool cancelled)
		{
			if (!IsLoading || _completionSource == null)
				return;
 
			TaskCompletionSource<bool> tcs = Interlocked.Exchange(ref _completionSource, null);
			if (tcs != null)
				tcs.SetResult(cancelled);
 
			lock (_synchandle)
			{
				CancellationTokenSource = null;
			}
		}
 
		private protected void OnLoadingStarted()
		{
			lock (_synchandle)
			{
				CancellationTokenSource = new CancellationTokenSource();
			}
		}
 
		protected void OnSourceChanged()
		{
			_weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(SourceChanged));
		}
 
		internal event EventHandler SourceChanged
		{
			add { _weakEventManager.AddEventHandler(value); }
			remove { _weakEventManager.RemoveEventHandler(value); }
		}
	}
}