summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core
diff options
context:
space:
mode:
authorE.Z. Hart <hartez@users.noreply.github.com>2016-08-02 14:54:57 -0600
committerJason Smith <jason.smith@xamarin.com>2016-08-02 13:54:57 -0700
commit34048b7bb4e8bf55c712ffd92303cf260c491c0e (patch)
tree19236fb4e72b8833c87e19fd15d0958b947764fc /Xamarin.Forms.Core
parent08e282350cb3f1c2345125682f14934111af2f9c (diff)
downloadxamarin-forms-34048b7bb4e8bf55c712ffd92303cf260c491c0e.tar.gz
xamarin-forms-34048b7bb4e8bf55c712ffd92303cf260c491c0e.tar.bz2
xamarin-forms-34048b7bb4e8bf55c712ffd92303cf260c491c0e.zip
Change SourceChanged event on ImageSource to weak event to allow Images (#268)
referencing application-wide StaticResource ImageSources to be GCed
Diffstat (limited to 'Xamarin.Forms.Core')
-rw-r--r--Xamarin.Forms.Core/Image.cs9
-rw-r--r--Xamarin.Forms.Core/ImageSource.cs12
-rw-r--r--Xamarin.Forms.Core/WeakEventManager.cs159
-rw-r--r--Xamarin.Forms.Core/Xamarin.Forms.Core.csproj1
4 files changed, 174 insertions, 7 deletions
diff --git a/Xamarin.Forms.Core/Image.cs b/Xamarin.Forms.Core/Image.cs
index 4292f952..dd2692ad 100644
--- a/Xamarin.Forms.Core/Image.cs
+++ b/Xamarin.Forms.Core/Image.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Reflection;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform;
@@ -7,8 +9,8 @@ namespace Xamarin.Forms
[RenderWith(typeof(_ImageRenderer))]
public class Image : View, IImageController
{
- public static readonly BindableProperty SourceProperty = BindableProperty.Create("Source", typeof(ImageSource), typeof(Image), default(ImageSource), propertyChanging: OnSourcePropertyChanging,
- propertyChanged: OnSourcePropertyChanged);
+ public static readonly BindableProperty SourceProperty = BindableProperty.Create("Source", typeof(ImageSource), typeof(Image), default(ImageSource),
+ propertyChanging: OnSourcePropertyChanging, propertyChanged: OnSourcePropertyChanged);
public static readonly BindableProperty AspectProperty = BindableProperty.Create("Aspect", typeof(Aspect), typeof(Image), Aspect.AspectFit);
@@ -126,6 +128,7 @@ namespace Xamarin.Forms
newvalue.SourceChanged += OnSourceChanged;
SetInheritedBindingContext(newvalue, BindingContext);
}
+
InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
}
@@ -138,7 +141,7 @@ namespace Xamarin.Forms
{
if (oldvalue == null)
return;
-
+
oldvalue.SourceChanged -= OnSourceChanged;
try
{
diff --git a/Xamarin.Forms.Core/ImageSource.cs b/Xamarin.Forms.Core/ImageSource.cs
index f65d0610..3b59f7fa 100644
--- a/Xamarin.Forms.Core/ImageSource.cs
+++ b/Xamarin.Forms.Core/ImageSource.cs
@@ -14,6 +14,8 @@ namespace Xamarin.Forms
TaskCompletionSource<bool> _completionSource;
+ readonly WeakEventManager _weakEventManager = new WeakEventManager();
+
protected ImageSource()
{
}
@@ -132,11 +134,13 @@ namespace Xamarin.Forms
protected void OnSourceChanged()
{
- EventHandler eh = SourceChanged;
- if (eh != null)
- eh(this, EventArgs.Empty);
+ _weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(SourceChanged));
}
- internal event EventHandler SourceChanged;
+ internal event EventHandler SourceChanged
+ {
+ add { _weakEventManager.AddEventHandler(nameof(SourceChanged), value); }
+ remove { _weakEventManager.RemoveEventHandler(nameof(SourceChanged), value);}
+ }
}
} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/WeakEventManager.cs b/Xamarin.Forms.Core/WeakEventManager.cs
new file mode 100644
index 00000000..226f329a
--- /dev/null
+++ b/Xamarin.Forms.Core/WeakEventManager.cs
@@ -0,0 +1,159 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace Xamarin.Forms
+{
+ internal class WeakEventManager
+ {
+ readonly Dictionary<string, List<Subscription>> _eventHandlers =
+ new Dictionary<string, List<Subscription>>();
+
+ public void AddEventHandler<TEventArgs>(string eventName, EventHandler<TEventArgs> handler)
+ where TEventArgs : EventArgs
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ AddEventHandler(eventName, handler.Target, handler.GetMethodInfo());
+ }
+
+ public void AddEventHandler(string eventName, EventHandler handler)
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ AddEventHandler(eventName, handler.Target, handler.GetMethodInfo());
+ }
+
+ public void HandleEvent(object sender, object args, string eventName)
+ {
+ var toRaise = new List<Tuple<object, MethodInfo>>();
+
+ List<Subscription> target;
+ if (_eventHandlers.TryGetValue(eventName, out target))
+ {
+ foreach (Subscription subscription in target)
+ {
+ object subscriber = subscription.Subscriber.Target;
+
+ if (subscriber == null)
+ {
+ // The subscriber was collected, so there's no need to keep this subscription around
+ target.Remove(subscription);
+ }
+ else
+ {
+ toRaise.Add(Tuple.Create(subscriber, subscription.Handler));
+ }
+ }
+ }
+
+ foreach (Tuple<object, MethodInfo> tuple in toRaise)
+ {
+ tuple.Item2.Invoke(tuple.Item1, new[] { sender, args });
+ }
+ }
+
+ public void RemoveEventHandler<TEventArgs>(string eventName, EventHandler<TEventArgs> handler)
+ where TEventArgs : EventArgs
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ RemoveEventHandler(eventName, handler.Target, handler.GetMethodInfo());
+ }
+
+ public void RemoveEventHandler(string eventName, EventHandler handler)
+ {
+ if (eventName == null)
+ {
+ throw new ArgumentNullException(nameof(eventName));
+ }
+
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ RemoveEventHandler(eventName, handler.Target, handler.GetMethodInfo());
+ }
+
+ void AddEventHandler(string eventName, object handlerTarget, MethodInfo methodInfo)
+ {
+ List<Subscription> target;
+ if (!_eventHandlers.TryGetValue(eventName, out target))
+ {
+ target = new List<Subscription>();
+ _eventHandlers.Add(eventName, target);
+ }
+
+ target.Add(new Subscription(new WeakReference(handlerTarget), methodInfo));
+ }
+
+ void RemoveEventHandler(string eventName, object handlerTarget, MemberInfo methodInfo)
+ {
+ List<Subscription> subscriptions;
+ if (!_eventHandlers.TryGetValue(eventName, out subscriptions))
+ {
+ return;
+ }
+
+ for (int n = subscriptions.Count; n > 0; n--)
+ {
+ Subscription current = subscriptions[n - 1];
+
+ if (current.Subscriber != handlerTarget
+ || current.Handler.Name != methodInfo.Name)
+ {
+ continue;
+ }
+
+ subscriptions.Remove(current);
+ }
+ }
+
+ struct Subscription
+ {
+ public Subscription(WeakReference subscriber, MethodInfo handler)
+ {
+ if (subscriber == null)
+ {
+ throw new ArgumentNullException(nameof(subscriber));
+ }
+
+ if (handler == null)
+ {
+ throw new ArgumentNullException(nameof(handler));
+ }
+
+ Subscriber = subscriber;
+ Handler = handler;
+ }
+
+ public readonly WeakReference Subscriber;
+ public readonly MethodInfo Handler;
+ }
+ }
+} \ No newline at end of file
diff --git a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
index d0cb293c..0541673f 100644
--- a/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
+++ b/Xamarin.Forms.Core/Xamarin.Forms.Core.csproj
@@ -331,6 +331,7 @@
<Compile Include="Internals\ToolbarTracker.cs" />
<Compile Include="ViewExtensions.cs" />
<Compile Include="ViewState.cs" />
+ <Compile Include="WeakEventManager.cs" />
<Compile Include="WeakReferenceExtensions.cs" />
<Compile Include="WebNavigatedEventArgs.cs" />
<Compile Include="WebNavigatingEventArgs.cs" />