I have a changing collection of changing objects(add/delete in Collection as well as property changes are also possible).
I want to get a new collection by some calculation on this Collection (summation / multiplication over some fields of all the objects having same value of a field). And bind calculated list to UI.
for Ex: So I have a list of rate and qty of different qualities of several fruits and now I want to find a new list having avg rate and total quantity of each fruit(irrespective of quality) given that rate & quantity can be changes at run time as well as some more qualities and fruit-species can be added to collection.
Now If rates or quantity changes at runtime in first List changes should be reflected in second list also.
Please suggest which methods should be used to achieve it considering we are binding the list to a Datagrid.
There is a neat Reactive Extension called Buffer:
var o = new ObservableCollection<long>();
var s1 = Observable.Interval(TimeSpan.FromMilliseconds(100)).Subscribe(o.Add);
var s2 =
Observable.FromEventPattern<NotifyCollectionChangedEventArgs>(o, "CollectionChanged")
.Buffer(TimeSpan.FromMilliseconds(500))
.Subscribe(
s =>
{
Console.WriteLine("Last received {0}. Current count: {1}", Convert.ToInt64(s.Last().EventArgs.NewItems[0]), o.Count);
});
This will allow you to receive on collection changed events as they occur, but ignore all except the last one.
UPDATE 1
It is a known issue that CollectionChanged
does not fire when items are updated. The MSDN documentation is simply incorrect. You will have to implement a TrulyObservableCollection
but your data elements must implement INotifyChanged
interface as well.
namespace ConsoleApplication1
{
#region
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading;
#endregion
internal class Program
{
#region Methods
private static void Main(string[] args)
{
var autoReset = new AutoResetEvent(false);
var r = new Random();
var o = new TrulyObservableCollection<DataPoint>();
var subscription1 = Observable.Interval(TimeSpan.FromSeconds(1)).Take(3).Subscribe(
i =>
{
o.Add(
new DataPoint
{
ItemCount = r.Next(100)
});
Console.WriteLine("Fire1 {0}", i);
});
var subscription2 =
Observable.FromEventPattern<NotifyCollectionChangedEventArgs>(o, "CollectionChanged")
.Subscribe(s => { Console.WriteLine("List changed. Current total {0}", o.Sum(s1 => s1.ItemCount)); });
var subscription3 = Observable.Interval(TimeSpan.FromSeconds(1)).Delay(TimeSpan.FromSeconds(3)).Take(3).Finally(
() =>
{
o.Clear();
autoReset.Set();
}).Subscribe(
i =>
{
if (o.Any())
{
o[r.Next(o.Count)].ItemCount = r.Next(100);
Console.WriteLine("Fire3 {0}", i);
}
});
autoReset.WaitOne();
}
#endregion
public class TrulyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
#region Constructors and Destructors
public TrulyObservableCollection() { CollectionChanged += this.TrulyObservableCollection_CollectionChanged; }
#endregion
#region Methods
private void TrulyObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
(item as INotifyPropertyChanged).PropertyChanged += this.item_PropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
(item as INotifyPropertyChanged).PropertyChanged -= this.item_PropertyChanged;
}
}
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var a = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
OnCollectionChanged(a);
}
#endregion
}
private class DataPoint : INotifyPropertyChanged
{
#region Fields
private int itemCount;
#endregion
#region Public Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Public Properties
public int ItemCount
{
get { return itemCount; }
set
{
itemCount = value;
this.OnPropertyChanged("ItemCount");
}
}
#endregion
#region Methods
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
}