ReactiveTabbedPage 数据绑定

ReactiveTabbedPage Data Binding

我已经将 ReactiveUI 与 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 不支持?

非常感谢任何建议!

将 VM 绑定到子页面的责任在于主机页面(即 ReactiveTabbedPage)。它自己知道哪个 VM 对应哪个视图。

让我们一步一个脚印。首先, MainViewModel:

public class MainViewModel : ReactiveObject
{
    public ChildViewModel1 Child1 => new ChildViewModel1();

    public ChildViewModel2 Child2 => new ChildViewModel2();
}

此代码显然不切实际,因为您不希望在每次 属性 访问时都重新创建子虚拟机。更重要的是 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);
            });
    }
}

我使用 WhenActivatedOneWayBind 调用以最安全的方式完成此操作。实际上,您的子 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 中的控件相关联。再一次,您可以针对不发生变化的属性对其进行优化。

就此示例而言,ChildView2ChildView1 大致相同,但显然可能完全不同。

最终结果如您所料:

从屏幕截图中看不出来但非常重要的是,当您切换离开时每个选项卡都会停用(如果它实现了 ISupportsActivation,其关联的视图模型也会停用)。这意味着您可以在不使用该选项卡时清除该选项卡的任何绑定和订阅,从而减少内存压力并提高性能。