我目前有一个使用MVVM的WPF项目。在我的项目中,我使用了一个静态类,它充当我的应用程序中各种控件所使用的集合的缓存,如下所示:
public static class AppCache
{
public static ObservableCollection<MyObject> MyObjects { get; set; }
private static async void GetMyObjectsAsync()
{
await Task.Run(() => GetMyObjects());
Mediator.Instance.NotifyColleagues("MyObjectsUpdatedMessage", true); // A helper class that implements messaging
}
private static GetMyObjects()
{
// Get objects via service call
...
MyObjects = result;
}
static AppCache()
{
MyObjects = new ObservableCollection<MyObject>();
GetMyObjectsAsync();
}
}
然后我在各种视图模型中订阅了调解器:
public class MyViewModel : ViewModelBase
{
...
public ObservableCollection<MyObject> MyObjects
{
// My ViewModelBase lets me implement INotifyPropertyChanged like this
get { return GetValue(() => MyObjects); }
set { SetValue(() => MyObjects, value); }
}
[Message("MyObjectsUpdatedMessage")]
private void OnMyObjectSourceUpdated(bool c)
{
MyObjects = AppCache.MyObjects;
}
public MyViewModel ()
{
Mediator.Instance.RegisterHandler<bool>("MyObjectsUpdatedMessage", OnMyObjectSourceUpdated);
}
}
我对此方法的问题是,当我使用ViewModel中的集合执行操作(例如,添加或编辑MyObject
)时,我必须返回并手动更新全局AppCache集合并进行制作确保它与ViewModel中的内容匹配,然后确保我使用此集合更新所有其他ViewModel以使用新值,因为不涉及绑定:MyOtherViewModel.MyObjects = AppCache.MyObjects
另一种方法是公开GetMyObjectsAsync()
并在我从ViewModel进行更改后让AppCache从数据库更新,然后使用Mediator使用该集合更新所有其他视图。我不喜欢这个,因为这意味着我最终打了一个我不想做的服务电话。
我想弄清楚的是,是否有任何方法可以使用Reactive Extensions来简化我的流程,这样我可以在我的ViewModel订阅的AppCache中定义某种Reactive Property,以及在更新时将更新推送到所有ViewModel,这是我可用的两个选项之间的混合(手动更新AppShared集合,但随后通知其所有子集,而不需要中介)。
我想我真正想要的是拥有一个基本上可绑定且可在ViewModels之间共享的属性。
我可以使用任何种类的反应性物质来实现这种目的吗?类似的东西:
编辑:
我能够让订阅工作如下,但这又类似于使用Mediator的选项,因为每当我更新MyObjects时我都必须调用NotifyMyObjectChanged
。有没有办法让MyObjectsObservable
'监听'更改MyObjects并自动调用NotifyMyObjectChanged
?如果没有办法做到这一点,那么在Mediator上使用RX是否有好处呢?
public static class AppCache
{
public static ObservableCollection<MyObject> MyObjects { get; set; }
public static IObservable<ObservableCollection<MyObject>> MyObjectsObservable => _mySubject; // C# 6 syntax
public static Subject<ObservableCollection<MyObject>> _mySubject { get; set; }
private static async void GetMyObjectsAsync()
{
await Task.Run(() => GetMyObjects());
NotifyMyObjectChanged() // this basically just replaces the mediator
}
private static GetMyObjects()
{
...
MyObjects = result;
}
private static void NotifyMyObjectChanged()
{
_mySubject.OnNext(MyObjects);
}
static AppCache()
{
_mySubject = new Subject<ObservableCollection<MyObject>>();
GetMyObjectsAsync();
}
}
public class MyViewModel : ViewModelBase
{
ObservableCollection<MyObject> MyObjects
{
get { return GetValue(() => MyObjects); }
set { SetValue(() => MyObjects, value); }
}
IDisposable _subscription { get; }
MyViewModel()
{
_subscription = AppCache.MyObjectsObservable.Subscribe (HandleMyObjectChanged);
}
private void HandleMyObjectChanged(ObservableCollection<MyObject> myObjects)
{
MyObjects = myObjects;
}
public void Dispose()
{
_subscription.Dispose();
}
}
答案 0 :(得分:1)
所以你想要做的是这样的事情:
public static class AppCache
{
static AppCache()
{
_mySubject = new Subject<MyObject>();
}
private static void NotifyMyObjectChanged(MyObject object)
{
_mySubject.OnNext(object);
}
public static IObservable<MyObject> MyObjectsObservable
{
get { return _mySubject; }
}
}
public class MyViewModel : ViewModelBase
{
MyViewModel()
{
_subscription = AppCache.MyObjectsObservable.
.Where(x => x == value)
.Subscribe (HandleMyObjectChanged);
}
private void HandleMyObjectChanged(MyObject object)
{
... do work here ....
}
public void Dispose()
{
_subscription.Dispose();
}
}
在这种情况下,您在此处所做的就是向您的视图模型发送通知,告知您MyObject
上的内容已发生变化。您可以从处理程序中获取object
变量,并使用该变量将已更改的属性复制到视图模型中。或者,您可以发送一个“消息”类,其中包含属性列表及其新值,这可能比发送整个对象轻一点。
要记住的另一件事是Observable可能会在UI线程以外的线程上发送“事件”。有一个Subscribe
覆盖,允许您指定要使用的TaskScheduler
,因此您可以指定UI调度程序,这样您就不必自己进行编组。
正如我在评论中所说,有很多方法可以做到这一点,所以你必须四处寻找最适合你需求的东西,但希望这会给你一些指导。
<强>更新强>
所以这里有一些AppCache
代码的更新(来自你的编辑),但你的问题基本上就是想要摆脱中介并用RX替换它。最后,你仍然会得到某种类型的中介,你只需要决定RX是否给你的东西比你自己的中介实现更多。 我会(并且确实)使用RX,但它确实是一种偏好,所以你将不得不自己做一些研究。
这显示了Observable.FromEventPattern
函数的使用,它非常有用并为您提供了一个很好的快捷方式:
public static class AppCache
{
ObservableCollection<MyObject> MyObjects { get; set; }
public static IObservable<ObservableCollection<MyObject>> MyObjectsObservable { get; private set; }
private static async void GetMyObjectsAsync()
{
await Task.Run(() => GetMyObjects());
NotifyMyObjectChanged() // this basically just replaces the mediator
}
private static GetMyObjects()
{
...
MyObjects = result;
MyObjectsObservable = Observable.FromEventPattern(h=>MyObjects.CollectionChanged += h, h=>MyObjects.CollectionChanged -= h);
}
static AppCache()
{
GetMyObjectsAsync();
}
}
我想告诉你这个用法但是这个代码并不完美,如果订阅者在创建MyObjectsObservable
之前调用了订阅,显然它会爆炸。
这是另一个链接,向您展示了RX可以做的很多事情,对我来说这是一个很棒的工具:http://www.introtorx.com/uat/content/v1.0.10621.0/04_CreatingObservableSequences.html#