用于实现ReactUI.XamForms主详细页面的模式

时间:2018-09-01 19:29:21

标签: xamarin.forms navigation master-detail reactiveui

我目前在Xamarin Forms项目中工作,并试图将ReactiveUI与Master Detail Page导航标准Xamarin Forms项目一起使用。

在阅读了ReactiveUI网站上的文档以及Kent Boogaart的著作《 You,I和ReactiveUI》之后,我仍然对如何使Master Detail设置与ReactiveUI和Xamarin Forms一起用于应用程序导航感到迷惑。 / p>

我正在引导该应用程序,该应用程序会加载根视图(“主详细信息页面”):

internal sealed class AppBootstrap: ReactiveObject, IScreen
{
    public AppBootstrap()
    {
        RegisterDependencies();

        this
            .Router
            .NavigateAndReset
            .Execute(GetRootViewModel())
            .Subscribe();
    }

    public RoutingState Router { get; } = new RoutingState();

    public Page GetMainPage() => new RoutedViewHost();

    private void RegisterDependencies()
    {
        Locator.CurrentMutable.RegisterConstant(this, typeof(IScreen));

        /*View Model Registrations*/
        Locator.CurrentMutable.Register(() => new RootView(), typeof(IViewFor<RootViewModel>));
        Locator.CurrentMutable.Register(()=> new MenuView(), typeof(IViewFor<MenuViewModel>));
        Locator.CurrentMutable.Register(()=> new DetailView(), typeof(IViewFor<DetailViewModel>));

        /*Service Registrations*/
    }

    private IRoutableViewModel GetRootViewModel()
    {
        //Add check for login here
        return new RootViewModel(this);
    }
}

然后在我的RootViewModel中:

public sealed class RootViewModel: ViewModelBase
{
    private Page _masterPage;
    private Page _detailPage;

    public RootViewModel(IScreen hostScreen) : base(hostScreen)
    { }

    public Page MasterPage
    {
        get => _masterPage;
        set => this.RaiseAndSetIfChanged(ref _masterPage, value);
    }

    public Page DetailPage
    {
        get => _detailPage;
        set => this.RaiseAndSetIfChanged(ref _detailPage, value);
    }

    public override Action<CompositeDisposable> OnActivated()
    {
        //What do I do?
    }
}

在谷歌搜索和搜索堆栈溢出之后,这里似乎没有答案,'documentation'并没有太大帮助。 ReactiveUI网站上列出的样本以及所有在线博客文章都仅涉及单页导航;副手,这是大多数现实世界电话应用程序实现的更加标准的基于抽屉的导航。

任何关于操作的帮助或指向基于实际工作的“详细信息页面”导航的链接都将非常有帮助,因为我在尝试发现任何实质内容时都失败了。

1 个答案:

答案 0 :(得分:1)

您对缺少MasterDetailPage的文档是正确的。感谢您在网站上创建该问题。在我们进行更新之前,这是一种方法。

在此示例中,我假定详细信息页面的每个项目都具有完全相同的布局。即使这不适合您的用例,也很容易对其进行自定义以使其适用于各种布局。

我们不必每次选择一个项目都创建新的DetailViewModel和DetailPage,而是简单地交换模型。视图会侦听此更改并重新绑定。

public class MyMasterDetailViewModel : ReactiveObject, IRoutableViewModel
{
    private IScreen _hostScreen;

    public MyMasterDetailViewModel(IScreen hostScreen = null)
    {
        _hostScreen = hostScreen ?? Locator.Current.GetService<IScreen>();

        var cellVms = GetData().Select(model => new CustomCellViewModel(model));
        MyList = new ObservableCollection<CustomCellViewModel>(cellVms);

        // Set the first list item as the default detail view content.
        Detail = new DetailViewModel();
        Detail.Model = cellVms.First().Model;

        // Swap out the detail's model property every time the user selects an item.
        this.WhenAnyValue(x => x.Selected)
            .Where(x => x != null)
            .Subscribe(cellVm => Detail.Model = cellVm.Model);
    }

    private CustomCellViewModel _selected;
    public CustomCellViewModel Selected
    {
        get => _selected;
        set => this.RaiseAndSetIfChanged(ref _selected, value);
    }

    public DetailViewModel Detail { get; }

    public ObservableCollection<CustomCellViewModel> MyList { get; }
}

...

public partial class MyMasterDetailPage : ReactiveMasterDetailPage<MyMasterDetailViewModel>
{
    public MyMasterDetailPage()
    {
        InitializeComponent();

        ViewModel = new MasterDetailViewModel();
        Detail = new NavigationPage(new DetailPage(ViewModel.Detail));

        this.WhenActivated(
            disposables =>
            {
                this
                    .OneWayBind(ViewModel, vm => vm.MyList, v => v.MyListView.ItemsSource)
                    .DisposeWith(disposables);
                this
                    .Bind(ViewModel, vm => vm.Selected, v => v.MyListView.SelectedItem)
                    .DisposeWith(disposables);
                this
                    .WhenAnyValue(x => x.ViewModel.Selected)
                    .Where(x => x != null)
                    .Subscribe(
                        model =>
                        {
                            // Hide the master list every time the user selects an item
                            // and reset the SelectedItem "trigger."
                            MyListView.SelectedItem = null;
                            IsPresented = false;
                        })
                    .DisposeWith(disposables);
            });
    }
}

...

public class DetailViewModel : ReactiveObject
{
    private CustomData _model;

    public CustomData Model
    {
        get => _model;
        set => this.RaiseAndSetIfChanged(ref _model, value);
    }

    public string Title => Model.Title;
}

...

public partial class DetailPage : ReactiveContentPage<DetailViewModel>
{
    public DetailPage(DetailViewModel viewModel)
    {
        InitializeComponent();

        ViewModel = viewModel;

        this.WhenActivated(
            disposables =>
            {
                this
                    .WhenAnyValue(x => x.ViewModel.Model)
                    .Where(x => x != null)
                    .Subscribe(model => PopulateFromModel(model))
                    .DisposeWith(disposables);
            });
    }

    private void PopulateFromModel(MyModel model)
    {
        Title = model.Title;
        TitleLabel.Text = model.Title;
    }
}

...

<?xml version="1.0" encoding="utf-8" ?>
<rxui:ReactiveMasterDetailPage
         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:local="clr-namespace:XamFormsSandbox"
         x:Class="XamFormsSandbox.MyMasterDetailPage"
         x:TypeArguments="local:MyMasterDetailViewModel"
         NavigationPage.HasNavigationBar="False">
    <MasterDetailPage.Master>
        <ContentPage Title="Master">
            <StackLayout>
                <ListView x:Name="MyListView">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <local:CustomCell />
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </StackLayout>
        </ContentPage>
    </MasterDetailPage.Master>
</rxui:ReactiveMasterDetailPage>

请注意,我在上面的XAML中使用NavigationPage.HasNavigationBar="False"隐藏了RoutedViewHost导航栏。否则,我们将使两个导航栏彼此重叠。

更新

以下是示例项目(当前为PR)的链接:https://github.com/reactiveui/ReactiveUI/pull/1741