c# - ReactiveUI:IObservable.Transform() 不转发通知

标签 c# wpf reactiveui

在下面的代码片段中,似乎 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)
   ...

但是这不会改变输出。

最佳答案

Is this expected behaviour?

是的。符合ObservableCollection<T>仅监视集合(或流)的更改。

当然,您想要的东西很常见,而且幸运的是,很容易实现。来自 ReactiveUI documentation :

DynamicData supports change tracking for classes that implement the INotifyPropertyChanged interface — ReactiveObjects. For example, if you'd like to do a WhenAnyValue on each element in a collection of changing objects, use the AutoRefresh() DynamicData operator

只需添加 AutoRefresh运算符(operator)到管道并使用 Transform接受要设置的标志的运算符重载 transformOnRefresh为真。

sl2.Connect()
   .AutoRefresh(x => x.Bar.Baz)
   .Transform(x => x.Bar.Baz, true)
   ...
<小时/>

为了帮助调试,您可以分解 IChangeSet<string>及其 Change<string> s。

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

关于c# - ReactiveUI:IObservable.Transform() 不转发通知,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60552423/

相关文章:

c# - 单元格编辑后 DataGrid 刷新

c# - 如何检查表中是否存在特定类的实例?

c# - ObservableCollection<T> 未收到有关 <T> 属性更改的通知

c# - 使用 ObservableAsPropertyHelper<> 来自 ReactiveUI 搜索的多个绑定(bind)

c# - 测试 ReactiveCommand 和 ReactiveObject ViewModel

c# - 使用 ReactiveUI 的 BindTo() 更新 XAML 属性会生成警告

c# - 如何在 C# 中动态删除按钮?

c# - ASP.net 在 XDocument 中创建 XLink 节点

c# - 在 ViewModel 中覆盖 List.ToString()

c# - WPF + MVVM : Override autocomplete in DataGridComboBoxColumn