File: Internals\WeakEventProxy.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
using System;
using System.Collections.Specialized;
using System.ComponentModel;
 
// NOTE: warning disabled for netstandard projects
#pragma warning disable 0436
using MaybeNullWhenAttribute = System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute;
#pragma warning restore 0436
 
namespace Microsoft.Maui.Controls
{
	/// <summary>
	/// An abstract base class for subscribing to an event via WeakReference.
	/// See WeakNotifyCollectionChangedProxy below for sublcass usage.
	/// </summary>
	/// <typeparam name="TSource">The object type that has the event</typeparam>
	/// <typeparam name="TEventHandler">The event handler type of the event</typeparam>
	abstract class WeakEventProxy<TSource, TEventHandler>
		where TSource : class
		where TEventHandler : Delegate
	{
		WeakReference<TSource>? _source;
		WeakReference<TEventHandler>? _handler;
 
		public bool TryGetSource([MaybeNullWhen(false)] out TSource source)
		{
			if (_source is not null && _source.TryGetTarget(out source))
			{
				return source is not null;
			}
 
			source = default;
			return false;
		}
 
		public bool TryGetHandler([MaybeNullWhen(false)] out TEventHandler handler)
		{
			if (_handler is not null && _handler.TryGetTarget(out handler))
			{
				return handler is not null;
			}
 
			handler = default;
			return false;
		}
 
		public virtual void Subscribe(TSource source, TEventHandler handler)
		{
			_source = new WeakReference<TSource>(source);
			_handler = new WeakReference<TEventHandler>(handler);
		}
 
		public virtual void Unsubscribe()
		{
			_source = null;
			_handler = null;
		}
	}
 
	/// <summary>
	/// A "proxy" class for subscribing INotifyCollectionChanged via WeakReference.
	/// General usage is to store this in a member variable and call Subscribe()/Unsubscribe() appropriately.
	/// Your class should have a finalizer that calls Unsubscribe() to prevent WeakNotifyCollectionChangedProxy objects from leaking.
	/// </summary>
	class WeakNotifyCollectionChangedProxy : WeakEventProxy<INotifyCollectionChanged, NotifyCollectionChangedEventHandler>
	{
		public WeakNotifyCollectionChangedProxy() { }
 
		public WeakNotifyCollectionChangedProxy(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler)
		{
			Subscribe(source, handler);
		}
 
		void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
		{
			if (TryGetHandler(out var handler))
			{
				handler(sender, e);
			}
			else
			{
				Unsubscribe();
			}
		}
 
		public override void Subscribe(INotifyCollectionChanged source, NotifyCollectionChangedEventHandler handler)
		{
			if (TryGetSource(out var s))
			{
				s.CollectionChanged -= OnCollectionChanged;
			}
 
			source.CollectionChanged += OnCollectionChanged;
			base.Subscribe(source, handler);
		}
 
		public override void Unsubscribe()
		{
			if (TryGetSource(out var s))
			{
				s.CollectionChanged -= OnCollectionChanged;
			}
			base.Unsubscribe();
		}
	}
 
	/// <summary>
	/// A "proxy" class for subscribing INotifyPropertyChanged via WeakReference.
	/// General usage is to store this in a member variable and call Subscribe()/Unsubscribe() appropriately.
	/// Your class should have a finalizer that calls Unsubscribe() to prevent WeakNotifyPropertyChangedProxy objects from leaking.
	/// </summary>
	class WeakNotifyPropertyChangedProxy : WeakEventProxy<INotifyPropertyChanged, PropertyChangedEventHandler>
	{
		public WeakNotifyPropertyChangedProxy() { }
 
		public WeakNotifyPropertyChangedProxy(INotifyPropertyChanged source, PropertyChangedEventHandler handler)
		{
			Subscribe(source, handler);
		}
 
		void OnPropertyChanged(object? sender, PropertyChangedEventArgs e)
		{
			if (TryGetHandler(out var handler))
			{
				handler(sender, e);
			}
			else
			{
				Unsubscribe();
			}
		}
 
		public override void Subscribe(INotifyPropertyChanged source, PropertyChangedEventHandler handler)
		{
			if (TryGetSource(out var s))
			{
				s.PropertyChanged -= OnPropertyChanged;
			}
 
			source.PropertyChanged += OnPropertyChanged;
 
			base.Subscribe(source, handler);
		}
 
		public override void Unsubscribe()
		{
			if (TryGetSource(out var s))
			{
				s.PropertyChanged -= OnPropertyChanged;
			}
 
			base.Unsubscribe();
		}
	}
}