ReactiveUI:IObservable.Transform() 不转发通知
ReactiveUI: IObservable.Transform() does not forward Notifications
在下面的代码片段中,似乎 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
中,World->Space 的更改永远不会完成。在 sl2
中,此更改已完成。
(2)CollectionChangedEvent只有在添加了in 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
运算符添加到管道并使用接受标志的 Transform
运算符重载将 transformOnRefresh
设置为 true。
sl2.Connect()
.AutoRefresh(x => x.Bar.Baz)
.Transform(x => x.Bar.Baz, true)
...
注意,为了帮助调试,您可以分解 IChangeSet<string>
及其 Change<string>
。
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}");
})
...
在下面的代码片段中,似乎 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
中,World->Space 的更改永远不会完成。在 sl2
中,此更改已完成。
(2)CollectionChangedEvent只有在添加了in 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
运算符添加到管道并使用接受标志的 Transform
运算符重载将 transformOnRefresh
设置为 true。
sl2.Connect()
.AutoRefresh(x => x.Bar.Baz)
.Transform(x => x.Bar.Baz, true)
...
注意,为了帮助调试,您可以分解 IChangeSet<string>
及其 Change<string>
。
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}");
})
...