【发布时间】:2011-01-18 14:27:59
【问题描述】:
我一直在研究在 MVVM 框架中使用 Rx。这个想法是对内存数据集使用“实时”LINQ 查询,将数据投影到要绑定的视图模型中。
以前,这可以通过使用 INotifyPropertyChanged/INotifyCollectionChanged 和名为 CLINQ 的开源库来实现。 Rx 和 IObservable 的潜力在于使用主题类将更改的事件从源模型传播到视图,从而转移到更具声明性的 ViewModel。最后一步需要从 IObservable 转换为常规数据绑定接口。
问题在于 Rx 似乎不支持实体已从流中删除的通知。示例如下。
该代码显示了一个 POCO,它使用 BehaviorSubject 类作为字段状态。代码继续创建这些实体的集合并使用 Concat 将过滤器流合并在一起。这意味着对 POCO 的任何更改都会报告给单个流。
为此流设置了一个过滤器以过滤 Rating==0。当偶数发生时,订阅只是将结果输出到调试窗口。
在任何元素上设置 Rating=0 都会触发该事件。但是将 Rating 设置回 5 将不会看到任何事件。
在 CLINQ 的情况下,查询的输出将支持 INotifyCollectionChanged - 因此从查询结果中添加和删除的项目将触发正确的事件以指示查询结果已更改(添加或删除的项目)。
我能想到解决此问题的唯一方法是设置两个具有相反(双)查询的流。添加到相反流的项目意味着从结果集中删除。如果做不到这一点,我可以只使用 FromEvent 而不使任何实体模型可观察 - 这使得 Rx 更像是一个事件聚合器。有什么指点吗?
using System;
using System.ComponentModel;
using System.Linq;
using System.Collections.Generic;
namespace RxTest
{
public class TestEntity : Subject<TestEntity>, INotifyPropertyChanged
{
public IObservable<string> FileObservable { get; set; }
public IObservable<int> RatingObservable { get; set; }
public string File
{
get { return FileObservable.First(); }
set { (FileObservable as IObserver<string>).OnNext(value); }
}
public int Rating
{
get { return RatingObservable.First(); }
set { (RatingObservable as IObserver<int>).OnNext(value); }
}
public event PropertyChangedEventHandler PropertyChanged;
public TestEntity()
{
this.FileObservable = new BehaviorSubject<string>(string.Empty);
this.RatingObservable = new BehaviorSubject<int>(0);
this.FileObservable.Subscribe(f => { OnNotifyPropertyChanged("File"); });
this.RatingObservable.Subscribe(f => { OnNotifyPropertyChanged("Rating"); });
}
private void OnNotifyPropertyChanged(string property)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property));
// update the class Observable
OnNext(this);
}
}
public class TestModel
{
private List<TestEntity> collection { get; set; }
private IDisposable sub;
public TestModel()
{
this.collection = new List<TestEntity>() {
new TestEntity() { File = "MySong.mp3", Rating = 5 },
new TestEntity() { File = "Heart.mp3", Rating = 5 },
new TestEntity() { File = "KarmaPolice.mp3", Rating = 5 }};
var observableCollection = Observable.Concat<TestEntity>(this.collection.Cast<IObservable<TestEntity>>());
var filteredCollection = from entity in observableCollection
where entity.Rating==0
select entity;
this.sub = filteredCollection.Subscribe(entity =>
{
System.Diagnostics.Debug.WriteLine("Added :" + entity.File);
}
);
this.collection[0].Rating = 0;
this.collection[0].Rating = 5;
}
};
}
【问题讨论】:
-
"问题在于 Rx 似乎不支持实体已从流中移除的通知" - 这是因为 IObservable 不代表持久集合,仅代表异步值流.
标签: c# silverlight mvvm system.reactive