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
ItemsChangedevent - 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
ListFeed<T>— Concrete implementationListChange<T>— Change typesListFeedExtensions— LINQ operations