ReactiveUI: IObservable.Transform () не пересылает уведомления - PullRequest
3 голосов
/ 05 марта 2020

В приведенном ниже фрагменте, похоже, что transform(x => x.Bar.Baz) только пересылает изменения, если элементы в sl2 добавляются или удаляются, но не при изменении значения Baz. Это ожидаемое поведение? Как это изменить, чтобы все модификации были найдены в transformed?

SourceList<FooClass> sl2 = new SourceList<FooClass>();    
sl2.Connect()
        .Transform(x => x.Bar.Baz)
        .Bind(out ReadOnlyObservableCollection<String> transformed)
        .Subscribe( x=> { Console.WriteLine("CHANGE from Subscribe"); } );

Полный код читается как. Пример выполнения также доступен на GitHub

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;

namespace ReactivUI_Test
{
    class BarClass : ReactiveObject
    {
        [Reactive] public String Baz { get; set; } = "";

        public BarClass(String b) { Baz = b; }
        public BarClass() { Baz = "!!!"; }

        public override String ToString() { return Baz.ToString(); }
    }



    class FooClass : ReactiveObject
    {
        [Reactive] public BarClass Bar { get; set; } = new BarClass();

        public override String ToString() { return Bar.ToString(); }
    }


    class ViewModel: ReactiveObject
    {
        [Reactive] FooClass Foo { get; set; } = new FooClass();

        void PrintList<T> (IEnumerable<T> items)
        {
            foreach (T item in items)
            {
                Console.WriteLine(item.ToString());
            }
        }

        public ViewModel()
        {


            Console.WriteLine("===  ===");

            SourceList<FooClass> sl2 = new SourceList<FooClass>();

            FooClass fo1 = new FooClass() { Bar = new BarClass("Hello ") };
            FooClass fo2 = new FooClass() { Bar = new BarClass("World ") };
            FooClass fo3 = new FooClass() { Bar = new BarClass("Out ") };
            FooClass fo4 = new FooClass() { Bar = new BarClass("There ") };
            FooClass fo5 = new FooClass() { Bar = new BarClass("!!!!!!") };

            sl2.Add(fo1);
            sl2.Add(fo2);
            sl2.Add(fo3);
            sl2.Add(fo4);


            sl2.Connect()
                .Transform(x => x.Bar.Baz)
                .Bind(out ReadOnlyObservableCollection<String> transformed)
                .Subscribe( x=> { Console.WriteLine("CHANGE from Subscribe"); } );

            Console.WriteLine("=== Start ===");

            ((INotifyCollectionChanged)transformed).CollectionChanged += 
                new NotifyCollectionChangedEventHandler(( s,e) => Console.WriteLine("CHANGE from Event Handler"));


            Console.WriteLine("sl2: ");
            PrintList<FooClass>(sl2.Items);

            Console.WriteLine("transformed: ");
            PrintList<String>(transformed);


            Console.WriteLine("=== Send to Space ===");
            fo2.Bar.Baz = "Space";

            Console.WriteLine("sl2: ");
            PrintList<FooClass>(sl2.Items);

            Console.WriteLine("transformed: ");
            PrintList<String>(transformed);      

            Console.WriteLine("=== Add !!!! ===" );

            sl2.Add(fo5);

            Console.WriteLine("sl2: ");
            PrintList<FooClass>(sl2.Items);

            Console.WriteLine("transformed: ");
            PrintList<String>(transformed);

            Console.WriteLine("===  ===");

            Console.WriteLine("Finish");
            Console.ReadLine();

        }
    }


    class Program 
    {
        static void Main(string[] args)
        {
            ViewModel vm = new ViewModel();
        }
    }
}

Вывод не тот, на который я рассчитывал. Кажется, что .Transform(x => x.Bar.Baz) только перенаправляет обновления, элементы добавляются или удаляются из списка 'sl2'. Если есть изменения значения Bar.Baz, эти изменения не будут переданы в transformed.

Мой вывод:

===  ===
CHANGE from Subscribe
=== Start ===
sl2:
Hello
World
Out
There
transformed:
Hello
World
Out
There
=== Send to Space ===
sl2:
Hello
Space
Out
There
transformed:
Hello
World      <--- I would have hoped to have 'Space' here
Out
There
=== Add !!!! ===
CHANGE from Event Handler   <-- I would have hoped that event handlers are triggered at World->Space
CHANGE from Subscribe       <-- I would have hoped that event handlers are triggered at World->Space
sl2:
Hello
Space
Out
There
!!!!!!
transformed:
Hello
World    <--- I would have hoped to have 'Space' here
Out
There
!!!!!!
===  ===
Finish

Обратите внимание: (1) в transformed Смена Мира-> Пространство никогда не делается. В sl2 это изменение сделано.

(2) CollectionChangedEvent запускается только после добавления в Item добавлено.

Мой вопрос:

Как убедиться, что transform() устанавливает правильные уведомления?

Обновление

В зависимости от ответа Funk ниже, это должно быть исправлено с помощью:

sl2.Connect()
   .AutoRefresh(x => x.Bar.Baz)
   .Transform(x => x.Bar.Baz)
   ...

Однако это не меняет вывод.

1 Ответ

2 голосов
/ 11 марта 2020

Это ожидаемое поведение?

Это так. В соответствии с ObservableCollection<T> отслеживаются только изменения в коллекции (или потоке).

Конечно, то, что вы хотите, является довольно распространенным и, к счастью, легко достижимым. Из документации ReactiveUI :

DynamicData поддерживает отслеживание изменений для классов, которые реализуют интерфейс INotifyPropertyChanged - ReactiveObjects. Например, если вы хотите сделать WhenAnyValue для каждого элемента в коллекции изменяющихся объектов, используйте оператор AutoRefre sh () DynamicData

Просто добавьте оператор AutoRefresh к конвейер и использовать перегрузку оператора Transform, которая принимает флаг для установки transformOnRefresh в значение true.

sl2.Connect()
   .AutoRefresh(x => x.Bar.Baz)
   .Transform(x => x.Bar.Baz, true)
   ...

Примечание. Для помощи в отладке вы можете разложить IChangeSet<string> и * 1024. * с.

sl2.Connect()
   .AutoRefresh(x => x.Bar.Baz)
   .Transform(x => x.Bar.Baz, true)
   .Do(x =>
   {
       foreach (var c in x) Console.WriteLine($"sl2 ticks {c.Item.Current}");
   })
   ...
...