Вы правы насчет отсутствия документации для 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>
Обратите внимание, что я скрываю панель навигации RoutedViewHost с NavigationPage.HasNavigationBar="False"
в XAML выше. В противном случае у нас было бы две панели навигации друг над другом.
UPDATE
Вот ссылка на пример проекта (в настоящее время PR): https://github.com/reactiveui/ReactiveUI/pull/1741