ReactiveUI命令取消订阅

时间:2017-12-06 12:26:06

标签: c# .net wpf mvvm reactiveui

我有2个视图和2个ViewModel:

第一视图:

public partial class FirstView : Page
{
    FirstViewModel ViewModel;
    public FirstView()
    {
        ViewModel = new FirstViewModel();
        ViewModel.ShowSecondView.Subscribe(_ =>
        {              
            NavigationService.Navigate(new SecondView(ViewModel.ChildViewModel));
        });            

        this.DataContext = ViewModel;
        InitializeComponent();            
    }        
}

First ViewModel:

public class FirstViewModel
{
    SecondViewModel ChildViewModel;
    public ReactiveCommand<Unit, Unit> ShowSecondView { get; set; }
    public FirstViewModel()
    {
        ChildViewModel = new SecondViewModel();
        ShowSecondView = ReactiveCommand.Create(() => 
        {
             ChildViewModel.Reconfigure(...);  
        });          
    }        
}

第二视图:

public partial class SecondView : Page
{
    SecondViewModel ViewModel;
    public SecondView(SecondViewModel viewModel)
    {
        ViewModel = viewModel;
        ViewModel.GoBack.Subscribe(_ => 
        {
            DoSomethingHard();
            if(NavigationService != null)  NavigationService.GoBack();
        });
        this.DataContext = ViewModel;
        InitializeComponent();            
    }        
}

第二个ViewModel:

public class SecondViewModel
{
    public ReactiveCommand<Unit, Unit> GoBack { get; set; }
    public FirstViewModel()
    {
        VeryLongInitialization();
        GoBack = ReactiveCommand.Create(() => { });          
    }        
    public void Reconfigure(...)
    { ... }
}

因此,当我多次运行FirstViewModel.ShowSecondView并多次运行SecondViewModel.GoBack时,DoSomethingHard()也会在每个创建的SecondView上执行几次。

为什么我要在ChildViewModel创建一次FilstViewModel?因为创建SecondViewModel需要很长时间。我不会每次都重新创建SecondViewModel但只重新配置它。

我的问题是如何取消订阅ViewModel.GoBack.Subscribe中的SecondView

P.S。也许我不应该在SecondView中重新FirstView,而是重新配置它SecondViewModel

更新1 (感谢Julien Mialon)

我添加IDisposable goBackSubscr并且它有效!我是对的吗?

public partial class SecondView : Page
{
    SecondViewModel ViewModel;
    IDisposable goBackSubscr;
    public SecondView(SecondViewModel viewModel)
    {
        ViewModel = viewModel;
        goBackSubscr = ViewModel.GoBack.Subscribe(_ => 
        {
            DoSomethingHard();
            if(NavigationService != null)  NavigationService.GoBack();
            goBackSubscr.Dispose();
        });
        this.DataContext = ViewModel;
        InitializeComponent();            
    }        
}

5 个答案:

答案 0 :(得分:1)

在您的观看中使用WhenAcitvated: 在你的页面的构造函数中(它必须是IViewFor):

 this.WhenActivated(
     disposables =>
     {
         ViewModel.Command.Subscribe(...).(disposables);
     });

答案 1 :(得分:1)

subscribe方法返回一个IDisposable,你应该存储它并在你想要取消订阅时将其处理掉。

答案 2 :(得分:1)

感谢@Krzysztof Skowronek

我再次阅读有关IViewFor并解决我的问题

public partial class SecondView : Page, IViewFor<SecondViewModel>
{
    public SecondViewModel ViewModel { get; set; }
    object IViewFor.ViewModel { get => ViewModel; set { ViewModel = (SecondViewModel)value; } }
    public SecondView(SecondViewModel viewModel)
    {
        ViewModel = viewModel;
        this.WhenActivated(disposables =>
        {
            ViewModel.GoBack.Subscribe(_ =>
                {
                    DoSomethingHard();
                    if (NavigationService != null) NavigationService.GoBack();
                })
                .DisposeWith(disposables);               

            this.WhenAnyValue(p => p.ViewModel)
                .BindTo(this, x => x.DataContext)
                .DisposeWith(disposables);
        });        
    InitializeComponent();            
    }        
}

答案 3 :(得分:0)

我想我知道你要做什么。如果我是对的,你正在尝试创建某种类型的向导或显示一个可以移动到下一个视图模型的东西,然后可以先移动?如果是这种情况,那么也许你想重新思考你是如何做到的。 例如,为什么不管理包含两个视图模型的视图模型中的来回按钮,并且子视图模型仅在首次需要时创建一次。然后,您的导航按钮可以确定当前启用哪些视图模型,并根据逻辑启用。 在向导类型方案中,请考虑将基础视图模型基于子视图模型,该模型可能具有名为CanMoveNext()和CanMovePrior()的属性或方法。在内部,如果准备好,它可以返回true。更容易。

答案 4 :(得分:0)

我还建议通过激活和停用ViewModel来启用和禁用命令。

使用小助手:

    public static class DisabledCommand
    {
        public static readonly ReactiveCommand<Unit, Unit> Instance
            = ReactiveCommand.Create<Unit, Unit>(_ => Unit.Default, Observable.Return(false));
    }

您可以这样做:

public sealed class MyViewModel : ReactiveObject, IActivateableViewModel
{

    public MyViewModel()
    {
        this.WhenActivated(d =>
        {
            // Observable that returns boolean to define if button is enabled
            // ie. Observable.Create() or this.WhenAnyValue(...)
            var canDoSomething = ...;

            // enable command when VM gets activated
            DoSomething = ReactiveCommand.Create<Unit, Unit>(_ => Unit.Default, canDoSomething);

            // disable command when VM gets disabled
            Disposable.Create(() => DoSomething = DisabledCommand.Instance).DisposeWith(d);

            DoSomething
                .Select(_ => this) // select ViewModel
                .Do(vm =>
                { 
                    // ...
                })
                .ObserveOn(RxApp.MainThreadScheduler)
                .ExecuteOn(RxApp.TaskPoolScheduler)
                .Subscribe()
                .DisposeWith(d);
        });
    }

    [Reactive] public ReactiveCommand<Unit, Unit> DoSomething { get; set; } = DisabledCommand.Instance;
    public ViewModelActivator Activator { get; } = new ViewModelActivator();
}

然后在您看来:

public partial class MyView : Page, IViewFor<MyViewModel>
{
    public MyView()
    {
        InitializeComponent();

        this.WhenActivated(d =>
        {
            if(ViewModel == null) return;

            IDisposable current = Disposable.Empty;
            ViewModel.WhenAnyValue(vm => vm.DoSomething)
                .Select(_ => ViewModel)
                .Subscribe(vm => 
                {
                    current.Dispose();
                    current = vm.DoSomething
                        .Select(_ => vm)
                        .Do(vm_ =>
                        {
                            // do something on the UI
                        })
                        .ObserveOn(RxApp.MainThreadScheduler)
                        .SubscribeOn(RxApp.MainThreadScheduler)
                        .Subscribe();
                })
                .ObserveOn(RxApp.MainThreadScheduler)
                .SubscribeOn(RxApp.MainThreadScheduler)
                .Subscribe()
                .DisposeWith(d);

              Disposable.Create(() => current.Dispose()).DisposeWith(d);
        });
    }

    object IViewFor.ViewModel
    {
        get => ViewModel;
        set => ViewModel = value as MyViewModel;
    }

    public MyViewModel ViewModel
    {
        get => DataContext as MyViewModel;
        set => DataContext = value;
    }
}
相关问题