c# - Xamarin Forms/ReactiveUI Router - 关闭子视图后显示子视图并执行父视图模型代码
问题描述
在我的父 ViewModel 中,我使用以下代码导航到ChildView
使用 ReactiveUI 的路由
appBootstrapper.Router.Navigate.Execute(new ChildViewModel(HostScreen)).Subscribe();
此代码按预期工作。
但是,当子视图关闭时,我想执行一些额外的操作(让我们使用通过 bool ViewModel 属性隐藏活动指示器的示例),例如
ShowActivityIndicator = true;
appBootstrapper.Router.Navigate.Execute(new ChildViewModel(HostScreen)).Subscribe();
ShowActivityIndicator = false;
此代码未按预期执行,因为显示了子视图,然后ShowActivityIndicator = false;
立即执行。
如何重写代码以便在关闭后ShowActivityIndicator = false;
执行?谢谢!ChildView
更新
感谢 Glenn 的建议,我尝试使用该WhenNavigatingFromObservable().Subscribe(...)
技术,但 lambda 代码从未执行。我的代码如下...
var microsoftSignInVM = new ViewModel.MicrosoftSignInVM(HostScreen);
//
microsoftSignInVM.WhenNavigatingFromObservable().Subscribe((_) =>
{
ShowActivityIndicator = true;
//
Data.Synchronisation.SynchroniseLocalDataCacheAsync();
//
appBootstrapper.Router.Navigate.Execute(new ViewModel.SelectJobVM(HostScreen)).Subscribe();
//
ShowActivityIndicator = false;
});
//
appBootstrapper.Router.Navigate.Execute(microsoftSignInVM).Subscribe();
我在 lambda 中放置了一个断点,它永远不会被击中。
更新 2 - 更新
我创建了几个最小的测试类。这些在 WPF 项目中可以正常工作,但不能使用 XAMARIN 表单...
共享
测试AV.xaml.cs
namespace TestApp.Test
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestAV : ReactiveContentPage<TestAVM>
{
public TestAV()
{
InitializeComponent();
//
this.WhenActivated(
disposables =>
{
this.BindCommand(this.ViewModel, x => x.ClickCommand, x => x.button)
.DisposeWith(disposables);
});
}
}
}
测试AVM.cs
namespace TestApp.Test
{
public class TestAVM : ReactiveObject, IEnableLogger, IRoutableViewModel
{
public TestAVM(IScreen hostScreen)
{
_HostScreen = hostScreen;
//
TestBVM testBVM = new TestBVM(_HostScreen);
//
_ClickCommand = ReactiveCommand.CreateFromObservable(() => _HostScreen.Router.Navigate.Execute(testBVM).Select(_ => Unit.Default));
//
testBVM.WhenNavigatingFromObservable().Subscribe((_) =>
{
ShowActivityIndicator = true;
});
}
//
public bool ShowActivityIndicator { get; set; }
//
private readonly ReactiveCommand<Unit, Unit> _ClickCommand; public ReactiveCommand<Unit, Unit> ClickCommand => _ClickCommand;
//
private readonly IScreen _HostScreen; public IScreen HostScreen => _HostScreen;
//
public string UrlPathSegment => "TestA";
}
}
测试BV.xaml.cs
namespace TestApp.Test
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class TestBV : ReactiveContentPage<TestBVM>
{
public TestBV()
{
InitializeComponent();
//
this.WhenActivated(
disposables =>
{
this
.BindCommand(this.ViewModel, x => x.ClickCommand, x => x.button)
.DisposeWith(disposables);
});
}
}
}
测试BVM.cs
namespace TestApp.Test
{
public class TestBVM : ReactiveObject, IEnableLogger, IRoutableViewModel
{
public TestBVM(IScreen hostScreen)
{
_HostScreen = hostScreen;
}
//
public ReactiveCommand<Unit, Unit> ClickCommand => _HostScreen.Router.NavigateBack;
//
private readonly IScreen _HostScreen; public IScreen HostScreen => _HostScreen;
//
public string UrlPathSegment => "TestB";
}
}
Xamarin 特定文件
AppBootstrapper.cs
namespace TestApp
{
public class AppBootstrapper : ReactiveObject, IScreen
{
public AppBootstrapper(IMutableDependencyResolver dependencyResolver = null, RoutingState router = null)
{
Router = router ?? new RoutingState();
//
RegisterParts(dependencyResolver ?? Locator.CurrentMutable);
//
Router.Navigate.Execute(new Test.TestAVM(this));
}
public RoutingState Router { get; private set; }
private void RegisterParts(IMutableDependencyResolver dependencyResolver)
{
dependencyResolver.RegisterConstant(this, typeof(IScreen));
//
dependencyResolver.Register(() => new Test.TestAV(), typeof(IViewFor<Test.TestAVM>));
dependencyResolver.Register(() => new Test.TestBV(), typeof(IViewFor<Test.TestBVM>));
}
public Page CreateMainPage()
{
return new RoutedViewHost();
}
}
}
测试AV.xaml
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vm="clr-namespace:TestApp.Test"
x:TypeArguments="vm:TestAVM"
x:Class="TestApp.Test.TestAV">
<ContentPage.Content>
<StackLayout>
<Button x:Name="button" Text="click A" HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
</rxui:ReactiveContentPage>
测试BV.xaml
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vm="clr-namespace:TestApp.Test"
x:TypeArguments="vm:TestBVM"
x:Class="TestApp.Test.TestBV">
<ContentPage.Content>
<StackLayout>
<Button x:Name="button" Text="click B" HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
</rxui:ReactiveContentPage>
WPF 特定文件
测试AV.xaml
<rxui:ReactiveUserControl x:Class="TestApp.TestAV"
x:TypeArguments="vm:TestAVM"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:rxui="http://reactiveui.net"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TestApp.Test">
<Grid>
<Button x:Name="button" Content="click A" />
</Grid>
</rxui:ReactiveUserControl>
测试BV.xaml
<rxui:ReactiveUserControl x:Class="TestApp.TestBV"
x:TypeArguments="vm:TestBVM"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:rxui="http://reactiveui.net"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:TestApp.Test">
<Grid>
<Button x:Name="button" Content="click B" />
</Grid>
</rxui:ReactiveUserControl>
值得注意的是,如果我使用
_ClickCommand = ReactiveCommand.CreateFromObservable(() => _HostScreen.Router.Navigate.Execute(new TestAVM()).Select(_ => Unit.Default));
在TestBVM.cs中,然后 lambda 按预期触发。
似乎 Xamarin Forms 的实现存在问题Router.NavigateBack
,但我愿意接受其他意见!
解决方案
您可以通过几种不同的方式来处理它。
当调用导航堆栈上的操作时,Router 有几个可以订阅的 observable。您还可以使用一个RoutableViewModelMixin
名为的扩展方法。WhenNavigatingFromObservable
public void ShowChildView()
{
ShowActivityIndicator = true;
var childViewModel = new ChildViewModel(HostScreen);
// Use the extension method to get an observable when the child view is navigated away from, subscribe and set the variable.
childViewModel.WhenNavigatingFromObservable().Subscribe(x => ShowActivityIndicator = false);
appBootstrapper.Router.Navigate.Execute(childViewModel).Subscribe();
}
第二种选择,如果您不介意孩子身上发生的功能,您可以使用 WhenActivated() 机制
public ChildView()
{
this.WhenActivated(disposable =>
{
// do activation logic.
return new[] { Disposable.Create(() => DoDeactivationLogic());
}
推荐阅读
- javascript - 切换按钮处于关闭状态,尽管它的值为 true
- ios - 在 iOS 中完成后,Firebase 不会关闭 Microsoft 签名页面
- scala - Scala 中更好的功能输入/输出参数样式
- c++ - 如何修复 poco Poco::Net::TCPServerParams() valgrind 确定泄漏
- reactjs - 如何在 Material UI 中居中?
- c++ - 在生成 LLVM IR 时跟踪范围?
- php - 我想在多列但同一个表中插入多个复选框值
- string - 检查字符串中是否有小写字符
- java - 如何从object1中创建的object2调用object1的方法
- cypress - 未捕获的类型错误:无法使用柏树读取未定义的属性“开始”