Observable.FromEventPattern для ObservableCollection - PullRequest
0 голосов
/ 17 января 2019

У меня есть класс ClassWrapper и класс BaseClassWrapper. BaseClassWrapper имеет объект типа ClassDTO, а внутри него имеется коллекция ObservableCollection, которую я хочу «наблюдать». Когда я создаю объект типа "ClassWrapper" и добавляю элемент в коллекцию ClassWrapper.ClassDTO.MyCollection.Add(new OtherClass()), наблюдатель не работает.

Но если я создаю ClassDTO или ObservableCollection внутри ClassWrapper (не в BaseWrapper), он работает отлично. Почему это происходит?

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>();
}

1 Ответ

0 голосов
/ 17 января 2019

Я попробовал код и добавил новый элемент каждые 100 мс, и был ... озадачен, мягко говоря, пока не случайно завис над Throttle и не увидел:

Игнорирует элементы из наблюдаемой последовательности, за которой следует другой элемент в течение определенного относительного времени.

Я подозреваю, что, как и я, вы ожидали, что Throttle() вернет последний элемент в окне. Хотя его описание в ReactiveX.io равно

испускает предмет из Обсерватории, только если прошло определенное время без его испускания другого предмета

А в документации говорится:

Для потоков, которые никогда не имеют промежутков, больших или равных dueTime между элементами, результирующий поток не будет создавать никаких элементов.

На самом деле, я использовал его таким образом в прошлом, но каким-то образом я каждый раз переключаюсь по названию, пока не вспомню, что фактическая операция отбрасывает , а не регулирует.

Когда я замедлял таймер, например, каждые 300 мс, я начинал получать результаты.

Оператор, который возвращает последнее событие в окне: Sample , а не Throttle. Если это то, что вы хотите, вы должны использовать

.Sample( TimeSpan.FromMilliseconds(300))

вместо Throttle.

Используйте Throttle, если вы хотите обновить пользовательский интерфейс только после , когда уведомления перестают поступать в течение 250 мс

Обновление

Чтобы проверить это поведение, я создал консольное приложение. Я добавил несколько исправлений в код вопроса, чтобы он мог скомпилироваться:

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);
            });
    }
}

Метод приложения Main добавляет элемент каждые 100 мс. Через 250 мс после добавления последнего элемента генерируется одно событие уведомления и печатается сообщение:

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();
}    
...