ReactiveTabbedPage数据绑定

时间:2017-01-10 09:35:18

标签: xamarin.forms reactiveui

我一直在使用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中是否不受支持?

非常感谢任何建议!

1 个答案:

答案 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);
            });
    }
}

我使用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非常相似,但显然可能完全不同。

最终结果如您所愿:

animation of reactive tabbed control

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