From 377d24fd05e7fb597c4f9237c1596ed4fbf86f19 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Fri, 24 Mar 2017 12:58:28 -0600 Subject: [PATCH] Setup a ConditionalWeakTable in ListProxy to hold weak references so that a ListView will work when connected to a Weak Source (#802) --- Xamarin.Forms.Core.UnitTests/ListProxyTests.cs | 92 ++++++++++++++++++++++++++ Xamarin.Forms.Core/ListProxy.cs | 12 +++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/Xamarin.Forms.Core.UnitTests/ListProxyTests.cs b/Xamarin.Forms.Core.UnitTests/ListProxyTests.cs index 6fa85d2..bf7f6ee 100644 --- a/Xamarin.Forms.Core.UnitTests/ListProxyTests.cs +++ b/Xamarin.Forms.Core.UnitTests/ListProxyTests.cs @@ -422,5 +422,97 @@ namespace Xamarin.Forms.Core.UnitTests return Items.GetEnumerator (); } } + + [Test] + public void WeakToWeak() + { + WeakCollectionChangedList list = new WeakCollectionChangedList(); + var proxy = new ListProxy(list); + + Assert.True(list.AddObject()); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.IsTrue(list.AddObject()); + + proxy = null; + + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + Assert.IsFalse(list.AddObject()); + } + + public class WeakCollectionChangedList : List, INotifyCollectionChanged + { + List handlers = new List(); + + public WeakCollectionChangedList() + { + + } + public event NotifyCollectionChangedEventHandler CollectionChanged + { + add { handlers.Add(new WeakHandler(this, value)); } + remove { throw new NotImplementedException(); } + } + + + public bool AddObject() + { + bool invoked = false; + var me = new object(); + + foreach (var handler in handlers.ToList()) + { + if (handler.IsActive) + { + invoked = true; + handler.Handler.DynamicInvoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, me)); + } + else + { + handlers.Remove(handler); + } + } + + return invoked; + } + + class WeakHandler + { + WeakReference source; + WeakReference originalHandler; + + public bool IsActive + { + get { return this.source != null && this.source.IsAlive && this.originalHandler != null && this.originalHandler.IsAlive; } + } + + public NotifyCollectionChangedEventHandler Handler + { + get + { + if (this.originalHandler == null) + { + return default(NotifyCollectionChangedEventHandler); + } + else + { + return (NotifyCollectionChangedEventHandler)this.originalHandler.Target; + } + } + } + + public WeakHandler(object source, NotifyCollectionChangedEventHandler originalHandler) + { + this.source = new WeakReference(source); + this.originalHandler = new WeakReference(originalHandler); + } + } + } } } diff --git a/Xamarin.Forms.Core/ListProxy.cs b/Xamarin.Forms.Core/ListProxy.cs index c2f5c9d..b25da16 100644 --- a/Xamarin.Forms.Core/ListProxy.cs +++ b/Xamarin.Forms.Core/ListProxy.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; +using System.Runtime.CompilerServices; using Xamarin.Forms.Internals; namespace Xamarin.Forms @@ -12,6 +13,7 @@ namespace Xamarin.Forms readonly ICollection _collection; readonly IList _list; readonly int _windowSize; + readonly ConditionalWeakTable _sourceToWeakHandlers; IEnumerator _enumerator; int _enumeratorIndex; @@ -30,6 +32,7 @@ namespace Xamarin.Forms ProxiedEnumerable = enumerable; _collection = enumerable as ICollection; + _sourceToWeakHandlers = new ConditionalWeakTable(); if (_collection == null && enumerable is IReadOnlyCollection) _collection = new ReadOnlyListAdapter((IReadOnlyCollection)enumerable); @@ -40,7 +43,7 @@ namespace Xamarin.Forms var changed = enumerable as INotifyCollectionChanged; if (changed != null) - new WeakNotifyProxy(this, changed); + _sourceToWeakHandlers.Add(this, new WeakNotifyProxy(this, changed)); } public IEnumerable ProxiedEnumerable { get; } @@ -362,10 +365,15 @@ namespace Xamarin.Forms { readonly WeakReference _weakCollection; readonly WeakReference _weakProxy; + readonly ConditionalWeakTable _sourceToWeakHandlers; public WeakNotifyProxy(ListProxy proxy, INotifyCollectionChanged incc) { - incc.CollectionChanged += OnCollectionChanged; + _sourceToWeakHandlers = new ConditionalWeakTable(); + NotifyCollectionChangedEventHandler handler = new NotifyCollectionChangedEventHandler(OnCollectionChanged); + + _sourceToWeakHandlers.Add(proxy, handler); + incc.CollectionChanged += handler; _weakProxy = new WeakReference(proxy); _weakCollection = new WeakReference(incc); -- 2.7.4