简体   繁体   中英

Observable.FromEventPattern on ObservableCollection

I have a ClassWrapper class and a BaseClassWrapper class. The BaseClassWrapper has an object of type ClassDTO and inside it has an ObservableCollection that is what I want to "observe". When I create an object of type "ClassWrapper" and add an item to the collection ClassWrapper.ClassDTO.MyCollection.Add(new OtherClass()) the observer does not work.

But if I create ClassDTO or an ObservableCollection inside ClassWrapper (not in BaseWrapper) it works perfectly. Why does this happen?

public class ClassWrapper : BaseClassWrapper
{
    public ClassWrapper()
    {
        Observable.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>
                (x => ClassDTO.MyCollection.CollectionChanged += x, x => ClassDTO.MyCollection.CollectionChanged -= x)
            .Where(x => x.EventArgs.Action == NotifyCollectionChangedAction.Add ||
                        x.EventArgs.Action == NotifyCollectionChangedAction.Replace ||
                        x.EventArgs.Action == NotifyCollectionChangedAction.Remove)
            .Throttle(TimeSpan.FromMilliseconds(250))
            .Subscribe(x =>
            {
                RaisePropertyChanged(SomeProperty);
            });
    }
}

public abstract class BaseClassWrapper : ObservableObject // MVVM Light
{
    public ClassDTO ClassDTO { get; set; } = new ClassDTO();
}

public class ClassDTO
{
    public ObservableCollection<OtherClass> MyCollection { get; set; } = new ObservableCollection<OtherClass>();
}

I tried the code and added a new item every 100ms and was ... perplexed to say the least until I accidentally hovered over Throttle and saw :

Ignores elements from an observable sequence which are followed by another element within a specified relative time duration.

I suspect, like me, you expected Throttle() to return the last item in a window. Even though its description in ReactiveX.io is

only emit an item from an Observable if a particular timespan has passed without it emitting another item

And the documentation remarks say :

For streams that never have gaps larger than or equal to dueTime between elements, the resulting stream won't produce any elements.

In fact, I've used it this way in the past, but somehow I trip over the name each time until I remember that the actual operation is debouncing , not throttling.

When I slowed the timer to fire eg every 300 ms I started getting results.

The operator that returns the last event in a window is Sample , not Throttle. If that's what you want, you should use

.Sample( TimeSpan.FromMilliseconds(300))

instead of Throttle .

Use Throttle if you want to update the UI only after notifications stop coming for 250 ms

Update

To test this behaviour I created a console application. I added a couple of fixes to the question's code to allow it to compile :

public class ClassWrapper : BaseClassWrapper
{
    public string SomeProperty { get; set; }
    public ClassWrapper()
    {
        Observable.FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>
                (x => ClassDTO.MyCollection.CollectionChanged += x, x => ClassDTO.MyCollection.CollectionChanged -= x)
            .Where(x => x.EventArgs.Action == NotifyCollectionChangedAction.Add ||
                        x.EventArgs.Action == NotifyCollectionChangedAction.Replace ||
                        x.EventArgs.Action == NotifyCollectionChangedAction.Remove)
            .Throttle( TimeSpan.FromMilliseconds(250))
            .Subscribe(x =>
            {
                RaisePropertyChanged( ()=> SomeProperty);
            });
    }
}

The application's Main method adds an item every 100ms. 250ms after the last item is added, a single notification event is raised and a message gets printed:

static async Task Main(string[] args)
{
    Console.WriteLine("Started");
    var c = new ClassWrapper();
    c.PropertyChanged += (sender, e) =>
    {
        Console.WriteLine($"Collection has {c.ClassDTO.MyCollection.Count} items");
    };

    for (int i = 0; i < 100; i++)
    {
        c.ClassDTO.MyCollection.Add(new OtherClass());
        await Task.Delay(100);

    }

    Console.ReadKey();
}    

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM