Setup a ConditionalWeakTable in ListProxy to hold weak references so that a ListView will work when connected to a Weak Source (#802)

This commit is contained in:
Shane Neuville 2017-03-24 12:58:28 -06:00 коммит произвёл Jason Smith
Родитель 63af840804
Коммит 377d24fd05
2 изменённых файлов: 102 добавлений и 2 удалений

Просмотреть файл

@ -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<object>, INotifyCollectionChanged
{
List<WeakHandler> handlers = new List<WeakHandler>();
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);
}
}
}
}
}

Просмотреть файл

@ -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<ListProxy, WeakNotifyProxy> _sourceToWeakHandlers;
IEnumerator _enumerator;
int _enumeratorIndex;
@ -30,6 +32,7 @@ namespace Xamarin.Forms
ProxiedEnumerable = enumerable;
_collection = enumerable as ICollection;
_sourceToWeakHandlers = new ConditionalWeakTable<ListProxy, WeakNotifyProxy>();
if (_collection == null && enumerable is IReadOnlyCollection<object>)
_collection = new ReadOnlyListAdapter((IReadOnlyCollection<object>)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<INotifyCollectionChanged> _weakCollection;
readonly WeakReference<ListProxy> _weakProxy;
readonly ConditionalWeakTable<ListProxy, NotifyCollectionChangedEventHandler> _sourceToWeakHandlers;
public WeakNotifyProxy(ListProxy proxy, INotifyCollectionChanged incc)
{
incc.CollectionChanged += OnCollectionChanged;
_sourceToWeakHandlers = new ConditionalWeakTable<ListProxy, NotifyCollectionChangedEventHandler>();
NotifyCollectionChangedEventHandler handler = new NotifyCollectionChangedEventHandler(OnCollectionChanged);
_sourceToWeakHandlers.Add(proxy, handler);
incc.CollectionChanged += handler;
_weakProxy = new WeakReference<ListProxy>(proxy);
_weakCollection = new WeakReference<INotifyCollectionChanged>(incc);