试图了解如何将rx.net与现有MVVM项目

时间:2016-03-03 18:51:53

标签: c# wpf mvvm system.reactive reactive-programming

我目前有一个使用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();
    }
}

1 个答案:

答案 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#