2017-01-10 2 views
2

Я использовал ReactiveUI некоторое время с помощью Xamarin Forms, но я ударил кирпичную стену, пытаясь использовать ReactiveTabbedPage. Я не могу понять, как ViewModel будет привязан к ReactiveContentPage, которые являются дочерними элементами ReactiveTabbedPage.ReactiveTabbedPage Data Binding

Так, в качестве примера, я мог бы иметь следующий код XAML:

<ReactiveTabbedPage x:Name="TabbedPage"> 
    <local:Page1View x:Name="Page1" /> 
    <local:Page2View x:Name="Page2" /> 
</ReactiveTabbedPage> 

Где Page1View и Page2View оба типа ReactiveContentPage и Т является ассоциированным ViewModel.

То, что я ожидал, это то, что при переходе к ReactiveTabbedPage будет отображаться страница PageViewView, а ViewModel будет загружаться (так же, как если бы я непосредственно перешел к Page1View). Однако ViewModel никогда не вызывается (конструктор никогда не запускается и привязка данных не происходит).

Однако, оба объекта Page1View и Page2View выполняют визуализацию, и я могу видеть исходные данные, созданные в этих представлениях (например, текст по умолчанию для меток и т. Д.).

Я знаю, что материал ViewModel работает правильно, потому что, если я непосредственно перехожу к Page1View (например, не в ReactiveTabbedPage), все отображает, как я ожидаю.

Я что-то пропустил, или я об этом ошибаюсь? Или это просто не поддерживается в текущей версии RxUI?

Любые советы приветствуются!

ответ

5

Ответственность за привязку виртуальной машины к дочерним страницам лежит на главной странице (то есть ReactiveTabbedPage). Он сам знает, какая виртуальная машина соответствует какому виду.

Давайте сделаем это шаг за шагом. Прежде всего, 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> 

Обратите внимание, что говорит каждый из представлений детей. Нам нужно подключить виртуальные машины к этим мнениям, которые мы делаем в отделенном коде для 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 вызовов. На самом деле, маловероятно, что ваши дочерние виртуальные машины изменятся, поэтому прямое назначение их, а не привязка, будет полностью прекрасным.

Теперь наши детские взгляды могут быть брошены вместе.Вот 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 с элементами управления в пользовательском интерфейсе. И еще раз вы можете оптимизировать это для свойств, которые не мутируют.

Для целей настоящего примера ChildView2 - это то же самое, что и ChildView1, но, очевидно, это может быть совершенно иначе.

Конечный результат, как и следовало ожидать:

animation of reactive tabbed control

Что не видно из скриншота, но очень важно, что каждая вкладка деактивируется при переходе от него (как бы связанный с ним просмотреть модель, если она реализована ISupportsActivation). Это означает, что вы можете очистить любые привязки и подписки для этой вкладки, когда она не используется, уменьшая давление памяти и улучшая производительность.

+0

Это было не сразу очевидно, но прекрасно решает проблему! Хорошие новости о деактивации тоже! –