Вот мое решение для вас:
Func<ObservableCollection<int>,
Func<int, bool>,
IObservable<int>> getAddsWhere =
(oc, pred) =>
from ep in Observable
.FromEventPattern<NotifyCollectionChangedEventHandler,
NotifyCollectionChangedEventArgs>(
h => oc.CollectionChanged += h,
h => oc.CollectionChanged -= h)
where ep.EventArgs.Action == NotifyCollectionChangedAction.Add
from i in ep.EventArgs.NewItems.OfType<int>()
where pred(i)
select i;
var firsts = getAddsWhere(First, i => i % 2 == 0);
var seconds = getAddsWhere(Second, i => i % 3 == 0);
var boths = firsts.Merge(seconds);
boths.Subscribe(i => guicollection.Add(i));
Я проверил это, и оно работает так, как вы просили - 2, 3 и 4 заканчиваются на guicollection
.
РЕДАКТИРОВАТЬ: Изменено, чтобы показать, как обрабатывать все значения перечисления NotifyCollectionChangedAction
.
Перечисление NotifyCollectionChangedAction
имеет пять значений:
Add
Move
Remove
Replace
Reset
Ничего не делать для Move
- это просто внутренняя операция.
Коллекция NewItems
в NotifyCollectionChangedEventArgs
содержит значения для Add
& Replace
.
Коллекция OldItems
в NotifyCollectionChangedEventArgs
содержит значения для Remove
& Replace
.
Сложная операция - Reset
- которая происходит, когда в коллекцию вызывается Clear()
- потому что она не сообщает вам, какие элементы были очищены, а затем элементы уже были удалены к моменту возникновения события.
Таким образом, единственное решение - создать метод расширения, который возвращает IObservable<ObservableCollectionOperation<T>>
и отслеживает внутренние изменения, чтобы можно было выполнить серию удалений при вызове Clear
.
Прежде чем я дам много кода здесь, я покажу вам, как выглядит вызывающий код. Это довольно просто и прямо.
var FirstOps = First.ToOperations(i => i % 2 == 0);
var SecondOps = Second.ToOperations(i => i % 3 == 0);
var BothOps = FirstOps.Merge(SecondOps);
var subscription = BothOps.Subscribe(guicollection);
Очень аккуратно, а?
Класс ObservableCollectionOperation<T>
определяется следующим образом:
public class ObservableCollectionOperation<T>
{
public readonly T Value;
public readonly Operation Operation;
public static ObservableCollectionOperation<T> Add(T value)
{
return new ObservableCollectionOperation<T>(value, Operation.Add);
}
public static ObservableCollectionOperation<T> Remove(T value)
{
return new ObservableCollectionOperation<T>(value, Operation.Remove);
}
public ObservableCollectionOperation(T value, Operation operation)
{
this.Value = value;
this.Operation = operation;
}
public override int GetHashCode()
{
return this.Value.GetHashCode()
* (this.Operation == Operation.Add ? 1 : -1);
}
public override bool Equals(object obj)
{
if (obj is ObservableCollectionOperation<T>)
{
var other = obj as ObservableCollectionOperation<T>;
return this.Value.Equals(other.Value)
&& this.Operation.Equals(other.Operation);
}
return false;
}
}
Перечисление Operation
требуется для различения добавления и удаления элементов, и неудивительно, что оно выглядит следующим образом:
public enum Operation
{
Add,
Remove,
}
Теперь о методе расширения.
public static IObservable<ObservableCollectionOperation<T>>
ToOperations<T>(this ObservableCollection<T> @this)
{
return Observable.Create<ObservableCollectionOperation<T>>(o =>
{
var local = new List<T>(@this);
Func<NotifyCollectionChangedEventArgs,
ObservableCollectionOperation<T>[]>
getAdds = ea =>
{
var xs = new T[] { };
if (
ea.Action == NotifyCollectionChangedAction.Add
|| ea.Action == NotifyCollectionChangedAction.Replace)
{
xs = ea.NewItems.Cast<T>().ToArray();
local.AddRange(xs);
}
return xs
.Select(x =>
ObservableCollectionOperation<T>.Add(x))
.ToArray();
};
Func<NotifyCollectionChangedEventArgs,
ObservableCollectionOperation<T>[]>
getRemoves = ea =>
{
var xs = new T[] { };
if (
ea.Action == NotifyCollectionChangedAction.Remove
|| ea.Action == NotifyCollectionChangedAction.Replace)
{
xs = ea.OldItems.Cast<T>().ToArray();
Array.ForEach(xs, x => local.Remove(x));
}
return xs
.Select(x =>
ObservableCollectionOperation<T>.Remove(x))
.ToArray();
};
Func<NotifyCollectionChangedEventArgs,
ObservableCollectionOperation<T>[]>
getClears = ea =>
{
var xs = new T[] { };
if (ea.Action == NotifyCollectionChangedAction.Reset)
{
xs = local.ToArray();
local.Clear();
}
return xs
.Select(x =>
ObservableCollectionOperation<T>.Remove(x))
.ToArray();
};
var changes =
from ep in Observable
.FromEventPattern<NotifyCollectionChangedEventHandler,
NotifyCollectionChangedEventArgs>(
h => @this.CollectionChanged += h,
h => @this.CollectionChanged -= h)
let adds = getAdds(ep.EventArgs)
let removes = getRemoves(ep.EventArgs)
let clears = getClears(ep.EventArgs)
from x in clears.Concat(removes).Concat(adds).ToObservable()
select x;
return changes.Subscribe(o);
});
}
Я добавил перегруженный метод расширения, чтобы помочь с фильтрацией:
public static IObservable<ObservableCollectionOperation<T>>
ToOperations<T>(
this ObservableCollection<T> @this,
Func<T, bool> filter)
{
return @this.ToOperations().Where(op => filter(op.Value));
}
И, наконец, я создал вспомогательный метод, позволяющий воспроизводить наблюдаемые операции в «наблюдателе» ObservableCollection<T>
:
public static IDisposable
Subscribe<T>(
this IObservable<ObservableCollectionOperation<T>> @this,
ObservableCollection<T> observer)
{
return @this.Subscribe(op =>
{
switch (op.Operation)
{
case Operation.Add :
observer.Add(op.Value);
break;
case Operation.Remove :
observer.Remove(op.Value);
break;
}
});
}
Теперь, да, это обрабатывает удаления и работает с примером операции, которую вы дали. : -)