diff --git a/src/DynamicData/Cache/ObservableCacheEx.cs b/src/DynamicData/Cache/ObservableCacheEx.cs
index e532d15d..2f91f6a8 100644
--- a/src/DynamicData/Cache/ObservableCacheEx.cs
+++ b/src/DynamicData/Cache/ObservableCacheEx.cs
@@ -41,15 +41,6 @@ public static partial class ObservableCacheEx
/// This is a thin wrapper around Rx's Do operator. The adaptor receives each changeset
/// as a side effect; the changeset itself is forwarded downstream unmodified.
///
- ///
- /// EventBehavior
- /// - AddPassed to the adaptor, then forwarded.
- /// - UpdatePassed to the adaptor, then forwarded.
- /// - RemovePassed to the adaptor, then forwarded.
- /// - RefreshPassed to the adaptor, then forwarded.
- /// - OnErrorForwarded to the downstream observer. The adaptor is not called.
- /// - OnCompletedForwarded to the downstream observer.
- ///
///
/// or is .
///
@@ -488,7 +479,6 @@ public static IObservable> AutoRefreshOnObservableUpdateBuffered and included in the merged changeset.
/// - RemoveBuffered and included in the merged changeset.
/// - RefreshBuffered and included in the merged changeset.
- /// - OnErrorForwarded to the downstream observer.
/// - OnCompletedAny remaining buffered changes are flushed, then completion is forwarded.
///
/// Worth noting: The merged changeset may contain contradictory changes (e.g., Add then Remove for the same key). Downstream operators handle this correctly, but raw inspection of the changeset may be surprising.
@@ -546,8 +536,8 @@ public static IObservable> BatchIf(this
/// - UpdateBuffered while paused; forwarded immediately while active.
/// - RemoveBuffered while paused; forwarded immediately while active.
/// - RefreshBuffered while paused; forwarded immediately while active.
- /// - OnErrorForwarded to the downstream observer. Buffered data is lost.
- /// - OnCompletedForwarded. Any remaining buffered data is flushed before completion.
+ /// - OnErrorBuffered data is lost.
+ /// - OnCompletedAny remaining buffered data is flushed before completion.
///
/// Worth noting: If the source completes while paused, buffered data IS flushed before OnCompleted. However, if the source errors while paused, buffered data is lost.
///
@@ -935,8 +925,6 @@ public static IObservable> BufferInitialUpdateCalls on the new value and emits an Update.
/// - RemoveEmits a Remove. The converter is not called.
/// - RefreshForwarded as Refresh. The converter is not called.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
///
@@ -967,8 +955,6 @@ public static IObservable> CastUpdate is called on the current item. An Update is emitted with the destination key. If the key selector produces a different destination key for the updated value than it did for the original value, downstream consumers will see an Update for a key that may not match the original Add.
/// - Remove is called on the item. A Remove is emitted with the destination key.
/// - Refresh is called on the item. A Refresh is emitted with the destination key.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
///
@@ -1069,8 +1055,6 @@ public static void Clear(this LockFreeObservableCacheUpdateThe previous item is removed from and the current item is added. Forwarded as Update.
/// - RemoveThe item is removed from . Forwarded as Remove.
/// - RefreshIgnored ( has no concept of refresh). Forwarded as Refresh.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
public static IObservable> Clone(this IObservable> source, ICollection target)
@@ -1145,15 +1129,6 @@ public static IObservable> ConvertThe source to defer until the first changeset arrives.
/// An observable that begins emitting changesets once the first non-empty changeset is received.
///
- ///
- /// EventBehavior
- /// - AddForwarded as Add once the initial non-empty changeset has been received.
- /// - UpdateForwarded as Update once loaded.
- /// - RemoveForwarded as Remove once loaded.
- /// - RefreshForwarded as Refresh once loaded.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
- ///
/// Worth noting: Blocks indefinitely if the cache or stream never receives any data. Ensure the source will eventually emit at least one changeset.
///
///
@@ -1314,8 +1289,6 @@ public static void EditDiff(this ISourceCache sour
/// - UpdateItems present in both snapshots that differ (per ) produce an Update.
/// - RemoveItems in the previous snapshot whose key is absent from the new snapshot produce a Remove.
/// - RefreshNot produced by this operator.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
/// or is .
@@ -1347,8 +1320,6 @@ public static IObservable> EditDiff(thi
/// - UpdateEmitted when the source produces Some(value) and an item was already tracked with a different value (per ).
/// - RemoveEmitted when the source produces None and an item was previously tracked.
/// - RefreshNot produced by this operator.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
/// or is .
@@ -1377,8 +1348,7 @@ public static IObservable> EditDiff(thi
/// - UpdateForwarded as Update if the key is unique within the changeset.
/// - RemoveForwarded as Remove if the key is unique within the changeset.
/// - RefreshForwarded as Refresh if the key is unique within the changeset.
- /// - OnErrorForwarded. Also emitted with if duplicate keys are detected in a changeset.
- /// - OnCompletedForwarded to the downstream observer.
+ /// - OnErrorAlso emitted with if duplicate keys are detected in a changeset.
///
///
public static IObservable> EnsureUniqueKeys(this IObservable> source)
@@ -1509,8 +1479,8 @@ public static IObservable> Except(this
/// - UpdateResets the removal timer for the item. Forwarded as Update.
/// - RemoveCancels the removal timer. Forwarded as Remove.
/// - RefreshForwarded as Refresh. No timer change.
- /// - OnErrorForwarded. All pending timers are cancelled.
- /// - OnCompletedForwarded. All pending timers are cancelled.
+ /// - OnErrorAll pending timers are cancelled.
+ /// - OnCompletedAll pending timers are cancelled.
///
/// Worth noting: A return from means "never expire". Update changes reset the expiration timer.
///
@@ -1623,8 +1593,6 @@ public static IObservable>> ExpireAfter<
/// - UpdateFour outcomes: if both old and new values pass, an Update is emitted. If only the new value passes, an Add is emitted. If only the old value passed, a Remove is emitted. If neither passes, the change is dropped.
/// - RemoveIf the item was included downstream, a Remove is emitted. Otherwise dropped.
/// - RefreshThe predicate is re-evaluated. If the item now passes but previously did not, an Add is emitted. If it still passes, a Refresh is forwarded. If it no longer passes, a Remove is emitted. If it still fails, the change is dropped.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
/// Worth noting: Refresh events trigger re-evaluation, which can promote or demote items. Pair with for property-change-driven filtering.
///
@@ -1683,8 +1651,6 @@ public static IObservable> Filter(
/// - UpdateRe-evaluated. Four outcomes as with the static overload.
/// - RemoveIf the item was included downstream, a Remove is emitted. Otherwise dropped.
/// - RefreshRe-evaluated against the current state. May produce Add, Refresh, Remove, or be dropped.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
/// Worth noting: should emit an initial value immediately. Each emission triggers a full re-evaluation of all items, which can be expensive for large collections.
///
@@ -1754,8 +1720,6 @@ public static IObservable> Filter(
/// - UpdateFour outcomes: if both old and new values pass, an Update is emitted. If only the new value passes, an Add is emitted. If only the old value passed, a Remove is emitted. If neither passes, the change is dropped.
/// - RemoveIf the item was included downstream, a Remove is emitted. Otherwise dropped.
/// - RefreshDropped. Because items are assumed immutable, there is nothing to re-evaluate.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
public static IObservable> FilterImmutable(
@@ -2137,8 +2101,6 @@ public static IObservable> GroupUpdateThe group key is re-evaluated. If unchanged, an Update is emitted within the same group. If the key changed, the item is removed from the old group (emitting Remove) and added to the new group (emitting Add). An empty old group is removed.
/// - RemoveThe item is removed from its group. If the group becomes empty, the group itself is removed from the output.
/// - RefreshThe group key is re-evaluated. If unchanged, a Refresh is forwarded within the group. If the key changed, the item moves between groups (Remove from old, Add to new).
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
/// Worth noting: Each group is a live sub-cache that can be subscribed to independently. Subscribers
@@ -2201,8 +2163,6 @@ public static IObservable> GroupUpdateGroup key re-evaluated. Item may move between groups if the key changed.
/// - RemoveItem removed from its group. Empty groups are removed.
/// - RefreshGroup key re-evaluated. Item may move between groups.
- /// - OnErrorForwarded from source or from .
- /// - OnCompletedForwarded when the source completes.
///
///
///
@@ -2380,8 +2340,6 @@ public static IObservable> Gr
/// - UpdateIf group key unchanged, group snapshot re-emitted. If changed, item moves between groups; both affected groups emit new snapshots.
/// - RemoveItem removed from group. Updated snapshot emitted. Empty groups are removed.
/// - RefreshGroup key re-evaluated. If changed, item moves; affected group snapshots emitted.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
///
///
@@ -2632,8 +2590,6 @@ public static IObservable> InnerJoinManyUpdateForwarded unchanged.
/// - RemoveForwarded unchanged.
/// - RefreshCalls Evaluate() on the item, then forwards the change.
- /// - OnErrorForwarded to subscribers.
- /// - OnCompletedForwarded to subscribers.
///
///
public static IObservable> InvokeEvaluate(this IObservable> source)
@@ -2814,8 +2770,6 @@ public static IObservable> LeftJoinManyUpdateForwarded unchanged.
/// - RemoveForwarded unchanged.
/// - RefreshForwarded unchanged.
- /// - OnErrorForwarded to subscribers.
- /// - OnCompletedForwarded to subscribers.
///
///
/// is .
@@ -2904,7 +2858,6 @@ public static IObservable>> LimitSizeTo<
/// - RemoveDisposes the child subscription for the removed item.
/// - RefreshNo effect on subscriptions. The child observable continues unchanged.
/// - OnErrorErrors from child observables are silently swallowed (the child is unsubscribed). Errors from the source changeset stream terminate the merged output.
- /// - OnCompletedThe output completes only when the source completes and all active child observables have also completed.
///
/// Worth noting: The output is a plain , not a changeset stream. If you need merged changesets, use instead.
///
@@ -2970,7 +2923,6 @@ public static IObservable MergeMany(t
/// - UpdateIf the updating source currently owns the downstream value for this key, an Update is emitted. If a comparer is provided and the update causes a different source's value to become the best candidate, an Update is emitted with that other source's value.
/// - RemoveIf the removed value was the one published downstream, the operator scans all remaining sources for the same key. If another source still holds that key, an Update is emitted with the replacement value (selected by comparer if provided, otherwise the next available). If no other source holds the key, a Remove is emitted.
/// - RefreshIf the refreshed item matches the currently published value, the Refresh is forwarded. With a comparer, all sources are re-evaluated first; if a different value now wins, an Update is emitted instead of the Refresh.
- /// - OnErrorAn error from any source (outer or inner) terminates the entire merged output.
/// - OnCompletedFor dynamic overloads, the output completes when the outer observable completes and all subscribed inner observables have also completed. For static overloads, completion depends on the completable parameter (default ).
///
///
@@ -3834,8 +3786,6 @@ public static IObservable> NotEmpty(thi
/// - UpdateRe-evaluated. If the new item is , emit accordingly. If the old item was downstream but the new one is not, emit Remove.
/// - RemoveIf the item was downstream, emit Remove.
/// - RefreshIf the item is downstream, forwarded as Refresh.
- /// - OnErrorForwarded to subscribers.
- /// - OnCompletedForwarded to subscribers.
///
///
/// is .
@@ -4071,8 +4021,6 @@ public static IObservable> OnItemUpdatedUpdateIf the item is currently downstream, an Update is emitted.
/// - RemoveReference count decremented. If the count reaches zero (no source holds the key), a Remove is emitted. Otherwise no emission.
/// - RefreshIf the item is downstream, a Refresh is forwarded.
- /// - OnErrorAn error from any source terminates the combined output.
- /// - OnCompletedThe output completes when all sources have completed.
///
///
/// or is .
@@ -4224,6 +4172,7 @@ public static IDisposable PopulateFrom(this ISourceCache or is .
///
///
+ ///
public static IDisposable PopulateInto(this IObservable> source, ISourceCache destination)
where TObject : notnull
where TKey : notnull
@@ -4273,15 +4222,6 @@ public static IDisposable PopulateInto(this IObservableA function that projects the current snapshot to a result value.
/// An observable that emits a projected value after each changeset.
///
- ///
- /// EventBehavior
- /// - AddCache updated, then invoked and result emitted.
- /// - UpdateCache updated, then invoked and result emitted.
- /// - RemoveCache updated, then invoked and result emitted.
- /// - RefreshCache updated, then invoked and result emitted.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
- ///
/// Worth noting: The selector is called on every changeset, which can be chatty. The exposes the full cache state for LINQ-style queries.
///
/// or is .
@@ -4362,11 +4302,6 @@ public static IObservable> RefCount(thi
/// The item to refresh.
///
/// Convenience method that wraps a Refresh inside . A Refresh does not change data in the cache; it signals downstream operators (such as or ) to re-evaluate the item.
- ///
- /// EventBehavior
- /// - RefreshProduced for the specified item. Downstream operators re-evaluate this item against their current logic (filter predicate, sort comparer, group key selector, etc.).
- /// - OtherNo Add, Update, or Remove events are produced by this method.
- ///
///
/// is .
///
@@ -4422,11 +4357,6 @@ public static void Refresh(this ISourceCache sourc
/// The item to remove.
///
/// Convenience method that wraps a single-item removal inside . The key is extracted from the item using the cache's key selector.
- ///
- /// EventBehavior
- /// - RemoveProduced if the key exists in the cache. The removed value is included in the changeset.
- /// - OtherNo Add, Update, or Refresh events are produced by this method.
- ///
///
/// is .
///
@@ -4892,6 +4822,7 @@ public static IObservable> SortBy
/// The type of the key.
/// The source to prepend an empty changeset to.
/// An observable that emits an empty changeset first, then all source changesets.
+ ///
public static IObservable> StartWithEmpty(this IObservable> source)
where TObject : notnull
where TKey : notnull => source.StartWith(ChangeSet.Empty);
@@ -5074,15 +5005,6 @@ public static IObservable> Switch(this
/// An of changeset streams. The operator subscribes to the latest inner stream.
/// A changeset stream reflecting the items from the most recently emitted inner source.
///
- ///
- /// EventBehavior
- /// - AddForwarded from the active inner source.
- /// - UpdateForwarded from the active inner source.
- /// - RemoveForwarded from the active inner source.
- /// - RefreshForwarded from the active inner source.
- /// - OnErrorAn error from any inner source or the outer source terminates the stream.
- /// - OnCompletedCompletes when the outer source and the current inner source have both completed.
- ///
/// On switch: Remove is emitted for all items from the previous source, then Add for all items from the new source.
/// Worth noting: Each switch clears the entire downstream cache before populating from the new source. Subscribers see a full remove-then-add reset on every switch.
///
@@ -5102,6 +5024,7 @@ public static IObservable> Switch(this
/// The type of the key.
/// The source to materialize into a collection on each change.
/// An observable which emits the read only collection.
+ ///
public static IObservable> ToCollection(this IObservable> source)
where TObject : notnull
where TKey : notnull => source.QueryWhenChanged(query => new ReadOnlyCollectionLight(query.Items));
@@ -5195,8 +5118,6 @@ public static IObservable> ToObservableChangeSetUpdateEmits Optional.Some(newValue) if the new value differs from the previous per . Otherwise suppressed.
/// - RemoveEmits .
/// - RefreshEmits Optional.Some(value) if the value differs from the last emission per . Otherwise suppressed.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
/// Worth noting: No emission occurs if the key is not present at subscription time. To get an initial None when the key is absent, use the overload with initialOptionalWhenMissing: true.
///
@@ -5254,6 +5175,7 @@ public static IObservable> ToObservableOptional
/// The sort function.
/// The sort order. Defaults to ascending.
/// An observable which emits the read only collection.
+ ///
public static IObservable> ToSortedCollection(this IObservable> source, Func sort, SortDirection sortOrder = SortDirection.Ascending)
where TObject : notnull
where TKey : notnull
@@ -6279,8 +6201,6 @@ static IEnumerable> ReplaceMoves(IChangeSet
/// - UpdateThe item is replaced in the collection snapshot. Condition recalculated.
/// - RemovePer-item subscription disposed. Condition recalculated over remaining items.
/// - RefreshNo effect on per-item subscriptions. Condition not recalculated unless the per-item observable emits.
- /// - OnErrorAn error from any per-item observable terminates the entire stream. Source errors also terminate.
- /// - OnCompletedCompletes when the source and all per-item observables have completed.
///
/// Worth noting: Items whose per-item observable has not yet emitted are treated as not satisfying the condition. An empty cache is vacuously . The result uses DistinctUntilChanged, so duplicate bool values are suppressed.
///
@@ -6332,8 +6252,6 @@ public static IObservable TrueForAll(this IObservab
/// - UpdateThe item is replaced in the collection snapshot. Condition recalculated.
/// - RemovePer-item subscription disposed. Condition recalculated over remaining items.
/// - RefreshNo effect on per-item subscriptions. Condition not recalculated unless the per-item observable emits.
- /// - OnErrorAn error from any per-item observable terminates the entire stream. Source errors also terminate.
- /// - OnCompletedCompletes when the source and all per-item observables have completed.
///
/// Worth noting: Items whose per-item observable has not yet emitted are treated as not satisfying the condition. An empty cache yields . The result uses DistinctUntilChanged, so duplicate bool values are suppressed.
///
@@ -6421,8 +6339,6 @@ public static IObservable> Watch(this IObse
/// - UpdateEmits the new value.
/// - RemoveEmits the removed item's value (not None; use if you need removal detection).
/// - RefreshEmits the current value.
- /// - OnErrorForwarded to the downstream observer.
- /// - OnCompletedForwarded to the downstream observer.
///
/// Worth noting: No emission occurs if the key is not present at subscription time. Changes to other keys are ignored entirely.
///
@@ -6473,7 +6389,6 @@ public static IObservable WatchValue(this IObservableRemoveDisposes the item's PropertyChanged subscription.
/// - RefreshNo effect on subscriptions.
/// - OnErrorErrors from individual property subscriptions are silently ignored. Source errors terminate the stream.
- /// - OnCompletedCompletes when the source changeset stream completes.
///
///
///
@@ -6513,7 +6428,6 @@ public static IObservable WatchValue(this IObservableRemoveDisposes the item's property subscription. No further emissions for this item.
/// - RefreshNo effect on subscriptions. The existing property subscription continues.
/// - OnErrorPer-item property subscription errors are silently ignored. Source errors terminate the stream.
- /// - OnCompletedCompletes when the source changeset stream completes.
///
///
///
@@ -6551,7 +6465,6 @@ public static IObservable> WhenPropertyChangedRemoveDisposes the property subscription.
/// - RefreshNo effect on subscriptions.
/// - OnErrorPer-item errors silently ignored. Source errors terminate the stream.
- /// - OnCompletedCompletes when the source completes.
///
///
///
@@ -6648,8 +6561,6 @@ public static IObservable> WhereReasonsAreNotUpdateIf the item is currently downstream (count is 1), an Update is emitted.
/// - RemoveReference count decremented. If the count drops to exactly 1, an Add is emitted (the item is now exclusive to one source). If it drops to 0, a Remove is emitted.
/// - RefreshIf the item is downstream, a Refresh is forwarded.
- /// - OnErrorAn error from any source terminates the combined output.
- /// - OnCompletedThe output completes when all sources have completed.
///
///
/// or is .
diff --git a/src/DynamicData/List/ListFilterPolicy.cs b/src/DynamicData/List/ListFilterPolicy.cs
index 69099808..8b347a76 100644
--- a/src/DynamicData/List/ListFilterPolicy.cs
+++ b/src/DynamicData/List/ListFilterPolicy.cs
@@ -10,14 +10,20 @@ namespace DynamicData;
public enum ListFilterPolicy
{
///
- /// Clear all items and replace with matches - optimised for large data sets.
- /// This option preserves order.
+ /// Clears all items and replaces with the new matches. Preserves order.
+ ///
+ /// Useful when downstream consumers (such as UI bindings) handle full resets more efficiently than individual
+ /// Add/Remove changes, or when the change set is expected to be very large relative to the source.
+ ///
///
ClearAndReplace,
///
- /// Calculate diff set - optimised for general filtering.
- /// This option does not preserve order.
+ /// Calculates the minimal diff between the previous and new result sets. Does not preserve order.
+ ///
+ /// Generally preferred for performance: only items whose inclusion status actually changed produce an Add or Remove.
+ /// Recommended for most scenarios.
+ ///
///
CalculateDiff
}
diff --git a/src/DynamicData/List/ObservableListEx.cs b/src/DynamicData/List/ObservableListEx.cs
index 2f871bf4..37f3a396 100644
--- a/src/DynamicData/List/ObservableListEx.cs
+++ b/src/DynamicData/List/ObservableListEx.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved.
+// Copyright (c) 2011-2025 Roland Pheasant. All rights reserved.
// Roland Pheasant licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.
@@ -24,17 +24,21 @@ namespace DynamicData;
public static class ObservableListEx
{
///
- /// Injects a side effect into a change set observable.
+ /// Injects a side effect into a changeset stream via an .
+ /// The adaptor's Adapt method is invoked for each changeset before it is forwarded downstream unchanged.
///
- /// The type of the item.
- /// The source.
- /// The adaptor.
- /// An observable which emits the change set.
- ///
- /// source
- /// or
- /// adaptor.
- ///
+ /// The type of items in the list.
+ /// The source to observe and adapt.
+ /// The adaptor whose Adapt method is invoked for each changeset.
+ /// A list changeset stream identical to the source, with the adaptor side effect applied.
+ /// or is .
+ ///
+ ///
+ /// This is the primary extension point for custom UI binding adaptors (e.g.,
+ /// delegates to this operator). If the adaptor throws, the exception propagates downstream as OnError.
+ ///
+ ///
+ ///
public static IObservable> Adapt(this IObservable> source, IChangeSetAdaptor adaptor)
where T : notnull
{
@@ -55,16 +59,21 @@ public static IObservable> Adapt(this IObservable
}
///
- /// Adds a key to the change set result which enables all observable cache features of dynamic data.
+ /// Adds a key to each item in a list changeset, converting it to a cache changeset that supports all keyed DynamicData operators.
///
+ /// The type of items in the list.
+ /// The type of the key.
+ /// The source to add keys to, converting to a cache changeset.
+ /// A function to extract a unique key from each item.
+ /// A cache changeset stream with keyed items.
+ /// or is .
///
- /// All indexed changes are dropped i.e. sorting is not supported by this function.
+ ///
+ /// All index information is dropped during conversion because cache changesets are unordered by default.
+ /// Use this when you need to transition from list-based pipelines to cache-based operators (Filter by key, Join, Group, etc.).
+ ///
///
- /// The type of object.
- /// The type of key.
- /// The source.
- /// The key selector.
- /// An observable which emits the change set.
+ ///
public static IObservable> AddKey(this IObservable> source, Func keySelector)
where TObject : notnull
where TKey : notnull
@@ -76,13 +85,33 @@ public static IObservable> AddKey(this
}
///
- /// Apply a logical And operator between the collections.
- /// Items which are in all of the sources are included in the result.
+ /// Applies a logical AND (intersection) between multiple list changeset streams.
+ /// Only items present in ALL sources appear in the result.
///
- /// The type of the item.
- /// The source.
- /// The others.
- /// An observable which emits the change set.
+ /// The type of items in the lists.
+ /// The first source to intersect.
+ /// The additional changeset streams to intersect with.
+ /// A list changeset stream containing items that exist in every source.
+ /// is .
+ ///
+ ///
+ /// Uses reference counting per item across all sources. An item appears downstream only when
+ /// its reference count is non-zero in ALL sources. Item identity is determined by the default equality comparer.
+ ///
+ ///
+ /// EventBehavior
+ /// - Add/AddRangeThe item's reference count is incremented in its source tracker. If the item is now present in all sources, an Add is emitted.
+ /// - ReplaceThe old item's reference count is decremented and the new item's is incremented. Depending on whether each is present in ALL sources, this emits an Add, Remove, Replace, or nothing.
+ /// - Remove/RemoveRange/ClearThe item's reference count is decremented. If it was in the result and is no longer in all sources, a Remove is emitted.
+ /// - RefreshForwarded as Refresh if the item is currently in the result.
+ /// - MovedIgnored (set operations are position-independent).
+ ///
+ /// Worth noting: Item identity uses object equality, not position. Duplicate items in a single source are reference-counted independently.
+ ///
+ ///
+ ///
+ ///
+ ///
public static IObservable> And(this IObservable> source, params IObservable>[] others)
where T : notnull
{
@@ -91,53 +120,49 @@ public static IObservable> And(this IObservable>
return source.Combine(CombineOperator.And, others);
}
- ///
- /// Apply a logical And operator between the collections.
- /// Items which are in all of the sources are included in the result.
- ///
- /// The type of the item.
- /// The sources.
- /// An observable which emits the change set.
+ ///
+ /// A of changeset streams to intersect.
+ ///
+ ///
+ /// This overload accepts a pre-built collection of sources instead of a params array.
+ ///
public static IObservable> And(this ICollection>> sources)
where T : notnull => sources.Combine(CombineOperator.And);
- ///
- /// Dynamically apply a logical And operator between the items in the outer observable list.
- /// Items which are in any of the sources are included in the result.
- ///
- /// The type of the item.
- /// The source.
- /// An observable which emits the change set.
+ ///
+ /// An of changeset streams. Sources can be added or removed dynamically.
+ ///
+ ///
+ /// This overload supports dynamic source management: adding or removing changeset streams from the observable list triggers re-evaluation.
+ ///
public static IObservable> And(this IObservableList>> sources)
where T : notnull => sources.Combine(CombineOperator.And);
- ///
- /// Dynamically apply a logical And operator between the items in the outer observable list.
- /// Items which are in any of the sources are included in the result.
- ///
- /// The type of the item.
- /// The source.
- /// An observable which emits the change set.
+ ///
+ /// An of . Each inner list's changes are connected automatically.
+ ///
+ ///
+ /// This overload accepts instances directly, calling Connect() internally.
+ ///
public static IObservable> And(this IObservableList> sources)
where T : notnull => sources.Combine(CombineOperator.And);
- ///
- /// Dynamically apply a logical And operator between the items in the outer observable list.
- /// Items which are in any of the sources are included in the result.
- ///
- /// The type of the item.
- /// The source.
- /// An observable which emits the change set.
+ ///
+ /// An of . Each inner list's changes are connected automatically.
+ ///
+ ///
+ /// This overload accepts instances directly, calling Connect() internally.
+ ///
public static IObservable> And(this IObservableList> sources)
where T : notnull => sources.Combine(CombineOperator.And);
///
- /// Converts the source list to an read only observable list.
+ /// Wraps a as a read-only , hiding mutation methods.
///
- /// The type of the item.
- /// The source.
- /// An observable list.
- /// source.
+ /// The type of items in the list.
+ /// The mutable source list to wrap.
+ /// A read-only observable list that mirrors the source.
+ /// is .
public static IObservableList AsObservableList(this ISourceList source)
where T : notnull
{
@@ -147,12 +172,21 @@ public static IObservableList AsObservableList(this ISourceList source)
}
///
- /// Converts the source observable to an read only observable list.
+ /// Materializes a changeset stream into a read-only .
+ /// The list is kept in sync with the source stream for the lifetime of the subscription.
///
- /// The type of the item.
- /// The source.
- /// An observable list.
- /// source.
+ /// The type of items in the list.
+ /// The source to materialize into a read-only list.
+ /// A read-only observable list reflecting the current state of the stream.
+ /// is .
+ ///
+ ///
+ /// This is the primary way to multicast a changeset pipeline. Materializing once into an ,
+ /// then calling Connect() on the result for each downstream consumer, ensures the upstream operators are evaluated only once
+ /// regardless of how many subscribers consume the result.
+ ///
+ ///
+ ///
public static IObservableList AsObservableList(this IObservable> source)
where T : notnull
{
@@ -162,14 +196,36 @@ public static IObservableList AsObservableList(this IObservable
- /// Automatically refresh downstream operators when any property changes.
+ /// Monitors all properties on each item (via ) and emits Refresh
+ /// changes when any property changes, causing downstream operators to re-evaluate.
///
- /// The type of object.
- /// The source observable.
- /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have successive property changes.
- /// When observing on multiple property changes, apply a throttle to prevent excessive refresh invocations.
- /// The scheduler.
- /// An observable change set with additional refresh changes.
+ /// The type of items, which must implement .
+ /// The source to monitor for property-driven refresh signals.
+ /// An optional buffer duration to batch multiple refresh signals into a single changeset.
+ /// An optional throttle applied to each item's property change notifications.
+ /// The scheduler for throttle and buffer timing. Defaults to .
+ /// A list changeset stream with additional Refresh changes injected when properties change.
+ /// is .
+ ///
+ ///
+ /// Wraps using WhenAnyPropertyChanged() as the re-evaluator.
+ /// Pair with or
+ /// to get reactive re-evaluation on property changes.
+ ///
+ ///
+ /// EventBehavior
+ /// - Add/AddRangeSubscribes to PropertyChanged on each new item. The original change is forwarded.
+ /// - ReplaceUnsubscribes from the old item, subscribes to the new. The original change is forwarded.
+ /// - Remove/RemoveRange/ClearUnsubscribes from removed items. The original change is forwarded.
+ /// - Moved/RefreshForwarded unchanged.
+ /// - Property changesA Refresh change is emitted for the item whose property changed.
+ ///
+ /// Worth noting: Each item generates a subscription. For large lists with frequent property changes, use and to reduce churn.
+ ///
+ ///
+ ///
+ ///
+ ///
public static IObservable> AutoRefresh(this IObservable> source, TimeSpan? changeSetBuffer = null, TimeSpan? propertyChangeThrottle = null, IScheduler? scheduler = null)
where TObject : INotifyPropertyChanged
{
@@ -190,16 +246,11 @@ public static IObservable> AutoRefresh(this IObserv
}
///
- /// Automatically refresh downstream operators when properties change.
+ /// Monitors a single property (selected by ) on each item via
+ /// and emits Refresh changes when that property changes, causing downstream operators to re-evaluate. More efficient than
+ /// the all-properties overload when only one property (of type ) affects downstream behavior.
///
- /// The type of object.
- /// The type of property.
- /// The source observable.
- /// Specify a property to observe changes. When it changes a Refresh is invoked.
- /// Batch up changes by specifying the buffer. This greatly increases performance when many elements have successive property changes.
- /// When observing on multiple property changes, apply a throttle to prevent excessive refresh invocations.
- /// The scheduler.
- /// An observable change set with additional refresh changes.
+ ///
public static IObservable> AutoRefresh(this IObservable> source, Expression> propertyAccessor, TimeSpan? changeSetBuffer = null, TimeSpan? propertyChangeThrottle = null, IScheduler? scheduler = null)
where TObject : INotifyPropertyChanged
{
@@ -221,15 +272,34 @@ public static IObservable> AutoRefresh(t
}
///
- /// Automatically refresh downstream operator. The refresh is triggered when the observable receives a notification.
+ /// Monitors each item with a custom observable and emits Refresh changes whenever that observable fires,
+ /// causing downstream operators (Filter, Sort, Group) to re-evaluate.
///
- /// The type of object.
- /// A ignored type used for specifying what to auto refresh on.
- /// The source observable change set.
- /// An observable which acts on items within the collection and produces a value when the item should be refreshed.
- /// Batch up changes by specifying the buffer. This greatly increases performance when many elements require a refresh.
- /// The scheduler.
- /// An observable change set with additional refresh changes.
+ /// The type of items in the list.
+ /// The type emitted by the re-evaluator observable (value is ignored).
+ /// The source to monitor for observable-driven refresh signals.
+ /// A factory that, given an item, returns an observable whose emissions trigger a Refresh for that item.
+ /// An optional buffer duration to batch refresh signals into a single changeset.
+ /// The for buffering.
+ /// A list changeset stream with additional Refresh changes injected when per-item observables fire.
+ /// or is .
+ ///
+ ///
+ /// This is the general-purpose refresh mechanism.
+ /// is a convenience wrapper that uses WhenAnyPropertyChanged() as the re-evaluator.
+ ///
+ ///
+ /// EventBehavior
+ /// - Add/AddRangeSubscribes to the re-evaluator observable for each new item. The original change is forwarded.
+ /// - ReplaceUnsubscribes from the old item's observable, subscribes to the new. The original change is forwarded.
+ /// - Remove/RemoveRange/ClearUnsubscribes from removed items. The original change is forwarded.
+ /// - Moved/RefreshForwarded unchanged.
+ /// - Re-evaluator firesThe item's current index is looked up and a Refresh change is emitted.
+ ///
+ ///
+ ///
+ ///
+ ///
public static IObservable> AutoRefreshOnObservable(this IObservable> source, Func> reevaluator, TimeSpan? changeSetBuffer = null, IScheduler? scheduler = null)
where TObject : notnull
{
@@ -240,18 +310,36 @@ public static IObservable> AutoRefreshOnObservable
- /// Binds a clone of the observable change set to the target observable collection.
+ /// Applies changeset mutations to a target for UI data binding.
///
- /// The type of the item.
- /// The source.
- /// The target collection.
- /// The reset threshold.
- /// An observable which emits the change set.
- ///
- /// source
- /// or
- /// targetCollection.
- ///
+ /// The type of items in the list.
+ /// The source to bind to a collection.
+ /// The target collection to keep in sync.
+ /// When a changeset exceeds this many changes, the collection is reset instead of applying individual changes.
+ /// A continuation of the source changeset stream (allows further chaining).
+ /// or is .
+ ///
+ ///
+ /// Delegates to with an internal collection adaptor.
+ /// Each changeset is applied to the target collection on the calling thread. For UI binding, ensure the source is
+ /// observed on the UI thread (e.g., via ObserveOn).
+ ///
+ ///
+ /// EventBehavior
+ /// - AddItem inserted at the specified index in the target collection.
+ /// - AddRangeItems inserted as a range. If the count exceeds , the collection is cleared and repopulated.
+ /// - ReplaceItem at the specified index is replaced.
+ /// - RemoveItem at the specified index is removed.
+ /// - RemoveRange/ClearItems removed from the collection.
+ /// - MovedItem is moved between positions in the collection.
+ /// - RefreshDepends on the adaptor implementation.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
public static IObservable> Bind(this IObservable> source, IObservableCollection targetCollection, int resetThreshold = BindingOptions.DefaultResetThreshold)
where T : notnull
{
@@ -270,18 +358,9 @@ public static IObservable> Bind(this IObservable>
}
///
- /// Binds a clone of the observable change set to the target observable collection.
+ /// Binds the source changeset stream to , with fine-grained control over reset threshold and other behaviors.
///
- /// The type of the item.
- /// The source.
- /// The target collection.
- /// The binding options.
- /// An observable which emits the change set.
- ///
- /// source
- /// or
- /// targetCollection.
- ///
+ ///
public static IObservable> Bind(this IObservable> source, IObservableCollection targetCollection, BindingOptions options)
where T : notnull
{
@@ -293,13 +372,15 @@ public static IObservable> Bind(this IObservable>
}
///
- /// Creates a binding to a readonly observable collection which is specified as an 'out' parameter.
+ /// Constructs a and binds the changeset stream to it.
+ /// Use this overload when you need a read-only view (typically for UI binding) without managing the backing collection yourself.
+ /// The created collection is returned via the output parameter.
///
- /// The type of the item.
- /// The source.
- /// The resulting read only observable collection.
- /// The reset threshold.
- /// A continuation of the source stream.
+ ///
+ ///
+ ///
+ /// The created collection is backed by an internal ObservableCollectionExtended<T>. Callers receive only the read-only wrapper.
+ ///
public static IObservable> Bind(this IObservable> source, out ReadOnlyObservableCollection readOnlyObservableCollection, int resetThreshold = BindingOptions.DefaultResetThreshold)
where T : notnull
{
@@ -316,13 +397,15 @@ public static IObservable> Bind(this IObservable>
}
///
- /// Creates a binding to a readonly observable collection which is specified as an 'out' parameter.
+ /// Constructs a and binds the changeset stream to it,
+ /// with fine-grained control over reset threshold and other behaviors.
+ /// The created collection is returned via the output parameter.
///
- /// The type of the item.
- /// The source.
- /// The resulting read only observable collection.
- /// The binding options.
- /// A continuation of the source stream.
+ ///
+ ///
+ ///
+ /// The created collection is backed by an internal ObservableCollectionExtended<T>. Callers receive only the read-only wrapper.
+ ///
public static IObservable> Bind(this IObservable> source, out ReadOnlyObservableCollection readOnlyObservableCollection, BindingOptions options)
where T : notnull
{
@@ -338,18 +421,9 @@ public static IObservable> Bind(this IObservable>
#if SUPPORTS_BINDINGLIST
///
- /// Binds a clone of the observable change set to the target observable collection.
+ /// Binds the source changeset stream to a WinForms , keeping in sync.
///
- /// The type of the item.
- /// The source.
- /// The target binding list.
- /// The reset threshold.
- ///
- /// source
- /// or
- /// targetCollection.
- ///
- /// An observable which emits the change set.
+ ///
public static IObservable> Bind<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(this IObservable> source, BindingList bindingList, int resetThreshold = BindingOptions.DefaultResetThreshold)
where T : notnull
{
@@ -361,30 +435,19 @@ public static IObservable> Bind(this IObservable>
#endif
- ///
- /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received.
- /// When a resume signal has been received the batched updates will be fired.
- ///
- /// The type of the object.
- /// The source.
- /// When true, observable begins to buffer and when false, window closes and buffered result if notified.
- /// The scheduler.
- /// An observable which emits the change set.
- /// source.
+ ///
+ ///
+ ///
+ /// This overload starts unpaused and has no timeout.
+ ///
public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, IScheduler? scheduler = null)
where T : notnull => BufferIf(source, pauseIfTrueSelector, false, scheduler);
- ///
- /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received.
- /// When a resume signal has been received the batched updates will be fired.
- ///
- /// The type of the object.
- /// The source.
- /// When true, observable begins to buffer and when false, window closes and buffered result if notified.
- /// if set to true [initial pause state].
- /// The scheduler.
- /// An observable which emits the change set.
- /// source.
+ ///
+ ///
+ ///
+ /// This overload allows setting the initial pause state but has no timeout.
+ ///
public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, bool initialPauseState, IScheduler? scheduler = null)
where T : notnull
{
@@ -394,32 +457,42 @@ public static IObservable> BufferIf(this IObservable
- /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received.
- /// When a resume signal has been received the batched updates will be fired.
- ///
- /// The type of the object.
- /// The source.
- /// When true, observable begins to buffer and when false, window closes and buffered result if notified.
- /// Specify a time to ensure the buffer window does not stay open for too long.
- /// The scheduler.
- /// An observable which emits the change set.
- /// source.
+ ///
+ ///
+ ///
+ /// This overload starts unpaused and accepts a timeout but not an explicit initial pause state.
+ ///
public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, TimeSpan? timeOut, IScheduler? scheduler = null)
where T : notnull => BufferIf(source, pauseIfTrueSelector, false, timeOut, scheduler);
///
- /// Batches the underlying updates if a pause signal (i.e when the buffer selector return true) has been received.
- /// When a resume signal has been received the batched updates will be fired.
+ /// Buffers changeset notifications while a pause signal is active, then flushes all buffered changes when resumed.
///
- /// The type of the object.
- /// The source.
- /// When true, observable begins to buffer and when false, window closes and buffered result if notified.
- /// if set to true [initial pause state].
- /// Specify a time to ensure the buffer window does not stay open for too long.
- /// The scheduler.
- /// An observable which emits the change set.
- /// source.
+ /// The type of items in the list.
+ /// The source to conditionally buffer.
+ /// An of that controls buffering: pauses (buffers), resumes (flushes).
+ /// The initial pause state. When , buffering starts immediately.
+ /// An optional maximum duration to keep the buffer open. After this time, the buffer is flushed regardless of pause state.
+ /// The for timeout scheduling.
+ /// A list changeset stream that buffers during pause and emits combined changesets on resume.
+ /// or is .
+ ///
+ ///
+ /// All changeset events are buffered at the changeset level (not individual changes) while paused.
+ /// On resume, all buffered changesets are emitted as a single combined changeset. If the buffer is empty on resume,
+ /// no emission occurs.
+ ///
+ ///
+ /// EventBehavior
+ /// - Any (while paused)Accumulated in an internal buffer. Not emitted downstream.
+ /// - Any (while active)Passed through immediately.
+ /// - Pause selector emits falseAll buffered changesets are flushed downstream as one combined changeset.
+ /// - Timeout firesAutomatically resumes and flushes the buffer.
+ /// - OnErrorForwarded immediately (not buffered).
+ /// - OnCompletedForwarded immediately.
+ ///
+ /// Worth noting: Each pause/resume cycle re-arms the timeout. Rapid toggling can create many small buffer windows.
+ ///
public static IObservable> BufferIf(this IObservable> source, IObservable pauseIfTrueSelector, bool initialPauseState, TimeSpan? timeOut, IScheduler? scheduler = null)
where T : notnull
{
@@ -430,13 +503,21 @@ public static IObservable> BufferIf(this IObservable
- /// Buffers changes for an initial period only. After the period has elapsed, not further buffering occurs.
+ /// Buffers changesets during an initial time window, then emits a single combined changeset and passes through subsequent changes.
///
- /// The type of object.
- /// The source change set.
- /// The period to buffer, measure from the time that the first item arrives.
- /// The scheduler to buffer on.
- /// An observable which emits the change set.
+ /// The type of items in the list.
+ /// The source to buffer during the initial loading period.
+ /// The time period (measured from first emission) during which changes are buffered.
+ /// The for timing the buffer window.
+ /// A list changeset stream where the initial burst is combined into one changeset.
+ ///
+ ///
+ /// For a configured duration after the first emission, all changesets are buffered and combined into a single emission.
+ /// After this initial window, subsequent changesets pass through immediately.
+ ///
+ ///
+ ///
+ ///
public static IObservable> BufferInitial(this IObservable> source, TimeSpan initialBuffer, IScheduler? scheduler = null)
where TObject : notnull => source.DeferUntilLoaded().Publish(
shared =>
@@ -447,11 +528,14 @@ public static IObservable> BufferInitial(this IObse
});
///
- /// Cast the changes to another form.
+ /// Casts each item in the changeset from object to using a direct cast.
///
- /// The type of the destination.
- /// The source.
- /// An observable which emits the change set.
+ /// The target type to cast to.
+ /// The source of object items.
+ /// A list changeset stream of cast items.
+ /// is .
+ ///
+ ///
public static IObservable> Cast(this IObservable> source)
where TDestination : notnull
{
@@ -461,14 +545,17 @@ public static IObservable> Cast(this IObs
}
///
- /// Cast the changes to another form.
- /// Alas, I had to add the converter due to type inference issues. The converter can be avoided by CastToObject() first.
+ /// Transforms each item in the changeset using a conversion function.
///
- /// The type of the object.
- /// The type of the destination.
- /// The source.
- /// The conversion factory.
- /// An observable which emits the change set.
+ /// The source item type.
+ /// The destination item type.
+ /// The source to cast.
+ /// A function to convert each item from to .
+ /// A list changeset stream of converted items.
+ /// or is .
+ /// Use this overload when type inference requires explicit specification of both source and destination types. Alternatively, call first, then the single-type-parameter overload.
+ ///
+ ///
public static IObservable> Cast(this IObservable> source, Func conversionFactory)
where TSource : notnull
where TDestination : notnull
@@ -481,22 +568,28 @@ public static IObservable> Cast(
}
///
- /// Cast the underlying type of an object. Use before a Cast function.
+ /// Casts each item in the changeset to object. Typically used before to work around type inference limitations.
///
- /// The type of the item.
- /// The source.
- /// An observable which emits the change set.
+ /// The source item type (must be a reference type).
+ /// The source to cast to object.
+ /// A list changeset stream of object items.
+ ///
public static IObservable> CastToObject(this IObservable> source)
where T : class => source.Select(changes => changes.Transform(t => (object)t));
///
- /// Clones the target list as a side effect of the stream.
+ /// Applies each changeset to the target list as a side effect, keeping it synchronized with the source.
///
- /// The type of the item.
- /// The source.
- /// The target of the clone.
- /// An observable which emits the change set.
- /// source.
+ /// The type of items in the list.
+ /// The source to clone.
+ /// The target list to clone changes into.
+ /// A continuation of the source changeset stream.
+ /// is .
+ ///
+ /// Lower-level than . Uses .Clone() to apply all changeset operations directly.
+ ///
+ ///
+ ///
public static IObservable> Clone(this IObservable> source, IList target)
where T : notnull
{
@@ -509,10 +602,10 @@ public static IObservable> Clone(this IObservable
/// Convert the object using the specified conversion function.
/// This is a lighter equivalent of Transform and is designed to be used with non-disposable objects.
///
- /// The type of the object.
- /// The type of the destination.
- /// The source.
- /// The conversion factory.
+ /// The type of items in the list.
+ /// The type of the destination items.
+ /// The source to convert.
+ /// The conversion factory.
/// An observable which emits the change set.
[Obsolete("Prefer Cast as it is does the same thing but is semantically correct")]
public static IObservable> Convert(this IObservable> source, Func conversionFactory)
@@ -527,11 +620,20 @@ public static IObservable> Convert
- /// Defer the subscription until the stream has been inflated with data.
+ /// Defers downstream delivery until the source emits its first changeset, then forwards all subsequent changesets.
///
/// The type of the object.
- /// The source.
- /// An observable which emits the change set.
+ /// The source to defer until the first changeset arrives.
+ /// A list changeset stream that begins emitting only after the source has produced its first changeset.
+ ///