Recognising when an item updates within an Observable[Range]Collection

I’m currently working on an app that needs to show the pictures and videos held on the user’s device for them to select from. I came across this great post by @XamBoy that I have used to get the media and save it in an ObservableRangeCollection.

NB an ObservableRangeCollection is an object that inherits from ObservableCollection, created by James Montemagno and available through his MvvmHelpers NuGet. Everything described below can be changed to extend just the ObservableCollection instead.

Now, what I need to do is allow a user to select one or many of the available media and show a neat little tick icon accordingly.

Except ObservableCollection (and therefore ObservableRangeCollection) does not recognise when an item has been changed. Per the Microsoft docs:-

Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.

Fortunately, I’ve come across this before in another project. First, I created a custom object which extends ObservableRangeCollection.

using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using MvvmHelpers;
namespace MyApp.Controls
{
public class ItemsChangeObservableRangeCollection<T> :
ObservableRangeCollection<T> where T : INotifyPropertyChanged
{
public delegate void ItemChangedEventHandler(object source, EventArgs args);
/// <summary>
/// Event fired when an item of the collection is updated
/// </summary>
public event ItemChangedEventHandler ItemChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
RegisterPropertyChanged(e.NewItems);
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
UnRegisterPropertyChanged(e.OldItems);
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
UnRegisterPropertyChanged(e.OldItems);
RegisterPropertyChanged(e.NewItems);
}
base.OnCollectionChanged(e);
}
protected override void ClearItems()
{
UnRegisterPropertyChanged(this);
base.ClearItems();
}
private void RegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void UnRegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
OnItemChange(sender);
}
protected virtual void OnItemChange(object sender)
{
ItemChanged?.Invoke(sender, EventArgs.Empty);
}
}
}

Then I amend my MediaAsset model to implement INotifyPropertyChanged and ensure the IsSelected property fires the OnPropertyChangedEvent.

using System.ComponentModel;
using System.Runtime.CompilerServices;
using MyWandle.Core.Enums;
namespace MyApp.Core.Models
{
public class MediaAsset : INotifyPropertyChanged
{
private bool _isSelected;
public string Id { get; set; }
public string Name { get; set; }
public MediaAssetType Type { get; set; }
public string PreviewPath { get; set; }
public string Path { get; set; }
public bool IsSelected
{
get => _isSelected;
set
{
if (Equals(value, _isSelected))
{
return;
}
_isSelected = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
view raw MediaAsset.cs hosted with ❤ by GitHub

Finally, I declare my collection of MediaAssets using my new object.

public ItemsChangeObservableRangeCollection MediaAssets
{
    get => _mediaAssets;
    set
    {
        _mediaAssets = value;
        RaisePropertyChanged(() => MediaAssets);
    }
}

Now, when I click on an image and update the IsSelected property for that asset I get my expected functionality:-

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s