CommunityToolkit / CommunityToolkit/dotnet

[Feature] Add Helper Extension `IList<T>.SyncWith(IList<T> another, IEqualityComparer<T> comparer = null)`

Open
#774 4 comments 0 reactions 0 assignees View on GitHub
feature request :mailbox_with_mail: needs triage :mag:
Dominant language
C#
Stars
3.8k
Forks
400
PR merge metrics
No merged PRs in 30d

Description

### Describe the problem

This might not be quite requiring, but it will indeed help a lot in some cases where we need to update the entire list `ListViewBase.ItemSource` without blinking the UI.

For example:

```csharp
var data = await SomeDataService.FetchDataAsync() // IList;
var oc = new ObservableCollection(data);
MyListView.ItemSource = oc; // Update the entire list.
```

Each time we update the entire data list (with data binding or not), the UI will blink, this is not terrible but it would be better if we can have an auto diff tool to make it visually updated with only different elements.

### Describe the solution

A helper extension `IList.SyncWith(IList another, IEqualityComparer comparer = null)` could be an initial idea:

```csharp
// Implementation
public static IList SyncWith(
this IList source,
IList another,
IEqualityComparer comparer = null)
{
// Step 1: Remove redundant items, if any.
source.Except(another).ToList().ForEach(x => _ = source.Remove(x));
// Step 2: Update the list, index by index.
for (var index = 0; index < another.Count; index++)
{
var newItem = another[index];
if (index < source.Count)
{
var oldItem = source[index];
if (comparer?.Equals(newItem, oldItem) ?? Equals(newItem, oldItem))
{
continue;
}

// Step 2-1: remove the old item, if it's been changed at the current position.
source.RemoveAt(index);
}

// Step 2-2: insert the new item at the current position.
source.Insert(index, newItem);
}

// Step 3: (last but not least) assertive verification.
Debug.Assert(source.SequenceEqual(another, comparer));
return source;
}

// Use Case
var data = await SomeDataService.FetchDataAsync(); // IList
var oc = new ObservableCollection(data);
MyListView.ItemSource = oc; // Initializes the entire list.

// When we need to update the entire list
data = await SomeDataService.FetchDataAsync(); // IList
_ = oc.SyncWith(data); // The UI won't blink and will apply the corresponding update animations.
```

### Alternatives

_No response_

### Additional info

_No response_

### Help us help you

Yes, I'd like to be assigned to work on this item.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.