我一直在使用ReamaUI与Xamarin Forms一段时间,但在尝试使用ReactiveTabbedPage时,我碰到了一堵砖墙。我无法弄清楚ViewModel将如何绑定到ReactiveTabbedPage的孩子们的ReactiveContentPage。
因此,举个例子,我可能会有以下XAML:
<ReactiveTabbedPage x:Name="TabbedPage">
<local:Page1View x:Name="Page1" />
<local:Page2View x:Name="Page2" />
</ReactiveTabbedPage>
其中Page1View和Page2View都是ReactiveContentPage类型,T是关联的ViewModel。
我期望发生的事情是,当ReactiveTabbedPage导航到时,将显示Page1View,并且将加载ViewModel(与我直接导航到Page1View时的方式相同)。但是,ViewModel永远不会被调用(构造函数永远不会被触发,也不会发生数据绑定)。
但是,Page1View和Page2View都会渲染,我可以看到在这些视图中创建的初始数据(例如标签的默认文本等)。
我知道ViewModel的工作正常,因为如果我直接导航到Page1View(例如不在ReactiveTabbedPage中),一切都会按照我的预期显示。
我是否遗漏了某些东西,或者我是否以错误的方式进行此操作?或者这在当前版本的RxUI中是否不受支持?
非常感谢任何建议!
答案 0 :(得分:8)
将VM绑定到子页面的责任在于主机页面(即ReactiveTabbedPage
)。它只知道哪个VM对应哪个视图。
让我们一步一个脚印。首先,MainViewModel
:
public class MainViewModel : ReactiveObject
{
public ChildViewModel1 Child1 => new ChildViewModel1();
public ChildViewModel2 Child2 => new ChildViewModel2();
}
这段代码显然不太现实,因为您不希望在每次访问属性时重新创建子VM。它更多的是与此相关的API。
ChildViewModel1
看起来像这样:
public class ChildViewModel1 : ReactiveObject
{
public string Test => "Hello";
}
ChildViewModel2
看起来大致相同。
现在我们可以开始设置视图了。我们的MainView.xaml
看起来像这样:
<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:TypeArguments="vms:MainViewModel"
xmlns:local="clr-namespace:ReactiveTabbedPageTest"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vms="clr-namespace:ReactiveTabbedPageTest.VMs"
x:Class="ReactiveTabbedPageTest.MainView">
<local:Child1View x:Name="child1View" Title="Child 1"/>
<local:Child2View x:Name="child2View" Title="Child 2"/>
</rxui:ReactiveTabbedPage>
请注意,它声明了每个子视图。我们需要将VM连接到这些视图,我们在MainView
的代码隐藏中执行这些视图:
public partial class MainView : ReactiveTabbedPage<VMs.MainViewModel>
{
public MainView()
{
InitializeComponent();
this.ViewModel = new VMs.MainViewModel();
this.WhenActivated(
disposables =>
{
this
.OneWayBind(this.ViewModel, x => x.Child1, x => x.child1View.ViewModel)
.DisposeWith(disposables);
this
.OneWayBind(this.ViewModel, x => x.Child2, x => x.child2View.ViewModel)
.DisposeWith(disposables);
});
}
}
我使用WhenActivated
和OneWayBind
电话以最安全的方式完成了这项工作。实际上,你的孩子VM不太可能会改变,所以直接分配它们而不是绑定是完全没问题的。
现在我们的孩子视图可以被抛在一起了。这是ChildView1.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"
x:Class="ReactiveTabbedPageTest.Child1View"
x:TypeArguments="vms:ChildViewModel1"
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
xmlns:vms="clr-namespace:ReactiveTabbedPageTest.VMs">
<Label x:Name="label" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
</rxui:ReactiveContentPage>
代码隐藏:
public partial class Child1View : ReactiveContentPage<ChildViewModel1>
{
public Child1View()
{
InitializeComponent();
this.WhenActivated(
disposables =>
{
this
.OneWayBind(this.ViewModel, x => x.Test, x => x.label.Text)
.DisposeWith(disposables);
});
}
}
我们再次执行通常的RxUI绑定优势,将VM中的属性与UI中的控件相关联。再一次,您可以针对不会发生变异的属性进行优化。
出于本示例的目的,ChildView2
与ChildView1
非常相似,但显然可能完全不同。
最终结果如您所愿:
屏幕截图中没有明显的内容,但非常重要的是,当您切换它时,每个标签都会停用(如果它实现了ISupportsActivation
,则与关联的视图模型一样)。这意味着您可以在不使用时清除该选项卡的任何绑定和订阅,从而减少内存压力并提高性能。