2

我需要将 2 个 ObservableCollection 合并为一个并将其绑定到 Grid 并且需要实时更新才能流向 Grid。对于.eg

ObservableCollection<int> First = new ObservableCollection<int>();
ObservableCollection<int> Second  = new ObservableCollection<int>();

//Some Rx Psuedo Code (p.s. this is not the actual code, this is where i need help)
{
    var guicollection = First
        .Where(i => i%2)
        .Merge(Second.Where(i => i % 3)).ToCollection();
}

listBox1.ItemsSource = guidcollection;

First.Add(1);
First.Add(2);
First.Add(3);
First.Add(4);
First.Add(5);
Second.Add(1);
Second.Add(2);
Second.Add(3);
Second.Add(4);

// Now the guicollection should have the following items 2,4 from FirstCollection
// and 3 from second collection

因此,当将对象添加到第一个或第二个集合时,上述 guicollection 应该实时工作,应该应用过滤并将过滤的项目添加到 guicollection。我在某处读到 Rx 框架可以在这里真正提供帮助。请帮我用实际的 Rx 代码替换上面的 Psudeo 代码。谢谢。

4

2 回答 2

3

这是我为您提供的解决方案:

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枚举有五个值:

  1. Add
  2. Move
  3. Remove
  4. Replace
  5. Reset

没有什么可做的Move——这只是一个内部操作。

NewItems集合 onNotifyCollectionChangedEventArgs包含Add&的值Replace

OldItems集合 onNotifyCollectionChangedEventArgs包含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;
        }
    });
}

现在,是的,这确实处理删除,它适用于您提供的示例操作。:-)

于 2011-09-14T10:21:07.550 回答
0

我对 Rx 框架一无所知,但是 ObservableCollections 会在集合的内容更改时通知 UI,因此您只需要从绑定的集合中添加/删除项目即可更新 UI

可以使用如下脚本完成合并:

public ObservableCollection<object> MergeCollections(
    ObservableCollection<object> first,
    ObservableCollection<object> second)
{
    foreach(var item in second)
    {
        if (!(first.Contains(item)))
            first.Add(item);
    }

    return first;
}
于 2011-08-24T16:15:51.137 回答