Table of Contents

IListFeed<T> Interface

Interface for reactive list feeds with incremental change tracking.

Namespace: DevBitsLab.Feeds
Assembly: DevBitsLab.Feeds.dll

public interface IListFeed<T> : IFeed<IReadOnlyList<T>>

Overview

IListFeed<T> extends IFeed for collections, providing:

  • Incremental changes via ItemsChanged event
  • CRUD operations — Add, Remove, Update, Move
  • Batch updates for atomic multi-item changes
  • Key-based updates for identity tracking

Properties

Property Type Description
Value IReadOnlyList<T> Current items (never null)
Count int Number of items
Inherited All IFeed<T> properties

Events

ItemsChanged

Fires with details about specific changes.

event EventHandler<ListChangedEventArgs<T>>? ItemsChanged;

Example:

productsFeed.ItemsChanged += (sender, e) => {
    switch (e.Change) {
        case ListChange<Product>.ItemAdded a:
            InsertRow(a.Index, a.Item);
            break;
        case ListChange<Product>.ItemRemoved r:
            RemoveRow(r.Index);
            break;
        case ListChange<Product>.ItemUpdated u:
            UpdateRow(u.Index, u.NewItem);
            break;
        case ListChange<Product>.ItemMoved m:
            MoveRow(m.OldIndex, m.NewIndex);
            break;
        case ListChange<Product>.ListReset reset:
            RebuildList(reset.NewItems);
            break;
        case ListChange<Product>.BatchChange batch:
            foreach (var c in batch.Changes) { /* ... */ }
            break;
    }
};

Methods

Add / Insert

void Add(T item);              // Adds to end
void Insert(int index, T item); // Inserts at position
products.Add(new Product { Name = "Widget" });
products.Insert(0, urgentProduct);  // Insert at beginning

Remove / RemoveAt

bool Remove(T item);     // Returns true if found
void RemoveAt(int index);
products.RemoveAt(0);
if (products.Remove(oldProduct)) {
    Console.WriteLine("Removed");
}

Update

Replace item at index or by key.

void Update(int index, T newItem);
bool Update<TKey>(T item, Func<T, TKey> keySelector);
// By index
products.Update(0, products.Value[0] with { Price = 19.99m });

// By key (finds and updates)
var updated = product with { Price = 19.99m };
if (products.Update(updated, p => p.Id)) {
    Console.WriteLine("Updated product");
}

Move

Reorder an item within the list.

void Move(int oldIndex, int newIndex);
products.Move(0, products.Count - 1);  // Move first to last

Clear / Reset

void Clear();                    // Remove all items
void Reset(IEnumerable<T> items); // Replace all items
products.Clear();
products.Reset(freshProductsFromApi);

BatchUpdate

Atomic multi-item changes with single notification.

void BatchUpdate(Action<IListFeedEditor<T>> updateAction);
products.BatchUpdate(editor => {
    editor.Add(product1);
    editor.Add(product2);
    editor.RemoveAt(0);
    editor.Update(3, updatedProduct);
});
// Single ItemsChanged with BatchChange

IListFeedEditor<T>

Editor interface for batch operations.

Method Description
Add(T item) Add to end
Insert(int index, T item) Insert at position
Remove(T item) Remove first occurrence
RemoveAt(int index) Remove at position
Update(int index, T item) Replace at position
Move(int old, int new) Reorder item
Clear() Remove all

Change Types

See ListChange<T> for complete details.

Type Properties
ItemAdded Item, Index
ItemRemoved Item, Index
ItemUpdated OldItem, NewItem, Index
ItemMoved Item, OldIndex, NewIndex
ListReset NewItems
BatchChange Changes

Applying Changes

Use ApplyTo to sync with another list:

IListFeed<T> (and ListFeed<T>) implement INotifyCollectionChanged, so UI layers can bind directly without mirroring into an ObservableCollection.


Common Patterns

UI Binding

Bind IListFeed<T> directly in XAML/WinUI/WPF because it already raises INotifyCollectionChanged:

public class ProductsViewModel
{
    public ListFeed<Product> Products { get; }

    public ProductsViewModel()
    {
        Products = ListFeed<Product>.Create(LoadProductsAsync);
    }
}

// XAML
// <ListView ItemsSource="{x:Bind ViewModel.Products}">
//   ...
// </ListView>

Upsert by key

Add or update in one call:

var products = ListFeed<Product>.FromItems(existingProducts);

// true when updated, false when added (appended)
var wasUpdated = products.Upsert(
    new Product { Id = 7, Name = "Updated" },
    p => p.Id);

// Delegate overload: avoid constructing the updated item unless the key exists
var wasUpdatedDeferred = products.Upsert(
    key: 7,
    createFunc: () => new Product { Id = 7, Name = "Created" },
    updateFunc: current => current with { Name = current.Name + "!" },
    keySelector: p => p.Id);

Filtered View

var allProducts = ListFeed<Product>.Create(LoadAllAsync);
var activeProducts = allProducts.Where(p => p.IsActive);

// activeProducts updates automatically when allProducts changes

Sorted View

var sortedByPrice = products.OrderBy(p => p.Price);
var sortedByName = products.OrderByDescending(p => p.Name);

See Also