Making ObservableCollection Thread-Safe in Xamarin.Forms
ObservableCollection
is the recommended collection to use for ListViews, but it isn't thread safe. Let's explore how we can fix this and use it in our multi-threaded apps!
The Xamarin.Forms team recommends using ObservableCollection
for ListView.ItemSource
, but when we then try to update the collection from different threads, we'll get this error because ObservableCollection
isn't thread safe:
System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: index
ListProxy.get_Item (System.Int32 index) D:\a\1\s\Xamarin.Forms.Core\ListProxy.cs:129
IList.get_Item (System.Int32 index)
Luckily, the fix is pretty easy!
Solution
There is a library included in Xamarin.Forms that we can use to ensure the ObservableCollection
is only updated by one thread at a time:
.BindingBase.EnableCollectionSynchronization
We just need to call this method in our constructor after initializing our ObservableCollection
and our collection becomes thread safe:
class MyViewModel
{
public MyViewModel()
{
MyCollection = new ObservableCollection<MyModel>();
Xamarin.Forms.BindingBase.EnableCollectionSynchronization(MyCollection, null, ObservableCollectionCallback);
}
public ObservableCollection<MyModel> MyCollection { get; }
void ObservableCollectionCallback(IEnumerable collection, object context, Action accessMethod, bool writeAccess)
{
// `lock` ensures that only one thread access the collection at a time
lock (collection)
{
accessMethod?.Invoke();
}
}
}