2011-08-24 107 views
2

我需要將2個ObservableCollection合併爲一個並將其綁定到網格,並需要實時更新才能流入網格。例如,合併兩個可觀察的集合並使用Rx綁定到列表框

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應該工作實時wheneve一個對象被添加到第一或第二集合中的濾波應該應用和經過濾項應該被添加到guicollection。我在某處讀到Rx框架在這裏真的可以幫到你。請幫助我用實際的Rx代碼替換上面的Psudeo代碼。謝謝。

+0

如果你需要多個'ItemsSource',你可以使用'CompositeCollection' –

+0

謝謝。 CompositeCollection是否也支持Where和實時更新。 –

+0

是的,它確實沒有 –

回答

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; 
} 
+0

謝謝。但我正在從Rx框架的角度來看待某些東西。 –

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,4結束於guicollection


編輯:更改爲顯示如何處理所有的NotifyCollectionChangedAction枚舉值。

NotifyCollectionChangedAction枚舉有五個值:

  1. Add
  2. Move
  3. Remove
  4. Replace
  5. 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; 
     } 
    }); 
} 

現在,是的,這是處理刪除,它適用於您提供的示例操作。 :-)

+0

這也會照顧到刪除。 –

+0

@ like.no.other - 不,它不會。刪除更困難。事實上,當你調用'ObservableCollection .Clear()''CollectionChanged'事件不會讓你知道清除的值,所以你需要保留你的訂閱集合的副本,調用。當你必須這樣做時,最好編寫你自己的擴展方法。我會編輯我的答案來向你展示。 – Enigmativity

+0

@Enigmativity這是一個很好的方法。謝謝。 –

相關問題