Add page routing and support various detail views (#1745)
samples: Add xamarin page routing and support various detail views (#1745)
This commit is contained in:
Родитель
fdec8e19b5
Коммит
0e8b0994a6
|
@ -49,7 +49,7 @@
|
|||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ReactiveUI" Version="*" />
|
||||
<PackageReference Include="ReactiveUI" Version="8.7.2" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="3.1.0.697729" />
|
||||
<PackageReference Include="Xamarin.Android.Support.Design" Version="27.0.2.1" />
|
||||
<PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="27.0.2.1" />
|
||||
|
|
|
@ -13,9 +13,9 @@ namespace MasterDetail
|
|||
{
|
||||
InitializeComponent();
|
||||
|
||||
Locator.CurrentMutable.Register(() => new CustomCell(), typeof(IViewFor<CustomCellViewModel>));
|
||||
var bootstrapper = new AppBootstrapper();
|
||||
|
||||
MainPage = new MainPage();
|
||||
MainPage = new MainPage(bootstrapper.CreateMainViewModel());
|
||||
}
|
||||
|
||||
protected override void OnStart()
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
using System;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class AppBootstrapper
|
||||
{
|
||||
public AppBootstrapper()
|
||||
{
|
||||
RegisterViews();
|
||||
RegisterViewModels();
|
||||
}
|
||||
|
||||
private void RegisterViews()
|
||||
{
|
||||
Locator.CurrentMutable.Register(() => new DummyPage(), typeof(IViewFor<DummyViewModel>));
|
||||
Locator.CurrentMutable.Register(() => new MasterCell(), typeof(IViewFor<MasterCellViewModel>));
|
||||
|
||||
// Detail pages
|
||||
Locator.CurrentMutable.Register(() => new NavigablePage(), typeof(IViewFor<NavigableViewModel>));
|
||||
Locator.CurrentMutable.Register(() => new NumberStreamPage(), typeof(IViewFor<NumberStreamViewModel>));
|
||||
Locator.CurrentMutable.Register(() => new LetterStreamPage(), typeof(IViewFor<LetterStreamViewModel>));
|
||||
}
|
||||
|
||||
public MainViewModel CreateMainViewModel()
|
||||
{
|
||||
// In a typical routing example the IScreen implementation would be this bootstrapper class.
|
||||
// However, a MasterDetailPage is designed to at the root. So, we assign the master-detail
|
||||
// view model to play the part of IScreen, instead.
|
||||
var viewModel = new MainViewModel();
|
||||
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
private void RegisterViewModels()
|
||||
{
|
||||
// Here, we use contracts to distinguish which routable view model we want to instantiate.
|
||||
// This helps us avoid a manual cast to IRoutableViewModel when calling Router.Navigate.Execute(...)
|
||||
Locator.CurrentMutable.Register(() => new NavigableViewModel(), typeof(IRoutableViewModel), typeof(NavigableViewModel).FullName);
|
||||
Locator.CurrentMutable.Register(() => new NumberStreamViewModel(), typeof(IRoutableViewModel), typeof(NumberStreamViewModel).FullName);
|
||||
Locator.CurrentMutable.Register(() => new LetterStreamViewModel(), typeof(IRoutableViewModel), typeof(LetterStreamViewModel).FullName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rxui:ReactiveViewCell x:Class="MasterDetail.CustomCell"
|
||||
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:MasterDetail"
|
||||
x:TypeArguments="local:CustomCellViewModel">
|
||||
|
||||
<Grid Padding="5,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="IconImage" />
|
||||
<Label x:Name="TitleLabel" Grid.Column="1" />
|
||||
</Grid>
|
||||
|
||||
</rxui:ReactiveViewCell>
|
|
@ -1,26 +0,0 @@
|
|||
using System.Reactive.Disposables;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public partial class CustomCell : ReactiveViewCell<CustomCellViewModel>
|
||||
{
|
||||
public CustomCell()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.WhenActivated(
|
||||
disposables =>
|
||||
{
|
||||
this
|
||||
.OneWayBind(ViewModel, vm => vm.Title, v => v.TitleLabel.Text)
|
||||
.DisposeWith(disposables);
|
||||
this
|
||||
.OneWayBind(ViewModel, vm => vm.IconSource, v => v.IconImage.Source, x => ImageSource.FromFile(x))
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
using ReactiveUI;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class CustomCellViewModel : ReactiveObject
|
||||
{
|
||||
public CustomCellViewModel(MyModel model)
|
||||
{
|
||||
Model = model;
|
||||
}
|
||||
|
||||
public MyModel Model { get; }
|
||||
|
||||
public string Title => Model.Title;
|
||||
|
||||
public string IconSource => Model.IconSource;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?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"
|
||||
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
|
||||
xmlns:local="clr-namespace:MasterDetail"
|
||||
x:Class="MasterDetail.LetterStreamPage"
|
||||
x:TypeArguments="local:LetterStreamViewModel">
|
||||
<ContentPage.Content>
|
||||
<StackLayout>
|
||||
<Label
|
||||
x:Name="LetterLabel"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
HorizontalOptions="CenterAndExpand"
|
||||
FontSize="36" />
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
</rxui:ReactiveContentPage>
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class LetterStreamPage : ReactiveContentPage<LetterStreamViewModel>
|
||||
{
|
||||
public LetterStreamPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.WhenActivated(
|
||||
disposables =>
|
||||
{
|
||||
this
|
||||
.OneWayBind(ViewModel, vm => vm.CurrentLetter, v => v.LetterLabel.Text)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class LetterStreamViewModel : ReactiveObject, IRoutableViewModel
|
||||
{
|
||||
private ObservableAsPropertyHelper<char> _currentLetter;
|
||||
|
||||
public LetterStreamViewModel()
|
||||
{
|
||||
_currentLetter = Observable
|
||||
.Interval(TimeSpan.FromSeconds(1))
|
||||
.Scan(64, (acc, current) => acc + 1)
|
||||
.Select(x => (char)x)
|
||||
.Take(26)
|
||||
.ToProperty(this, x => x.CurrentLetter, scheduler: RxApp.MainThreadScheduler);
|
||||
}
|
||||
|
||||
public char CurrentLetter => _currentLetter.Value;
|
||||
|
||||
public string UrlPathSegment => "Letter Stream Page";
|
||||
|
||||
public IScreen HostScreen { get; }
|
||||
}
|
||||
}
|
|
@ -3,12 +3,13 @@
|
|||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
|
||||
xmlns:local="clr-namespace:MasterDetail"
|
||||
x:Class="MasterDetail.MyDetailPage"
|
||||
x:TypeArguments="local:MyDetailViewModel">
|
||||
x:Class="MasterDetail.NavigablePage"
|
||||
x:TypeArguments="local:NavigableViewModel">
|
||||
<ContentPage.Content>
|
||||
<StackLayout>
|
||||
<Label
|
||||
x:Name="TitleLabel"
|
||||
<Button
|
||||
x:Name="NavigateButton"
|
||||
Text="Navigate"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
HorizontalOptions="CenterAndExpand" />
|
||||
</StackLayout>
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class NavigablePage : ReactiveContentPage<NavigableViewModel>
|
||||
{
|
||||
public NavigablePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.WhenActivated(
|
||||
disposables =>
|
||||
{
|
||||
this
|
||||
.BindCommand(ViewModel, vm => vm.NavigateToDummyPage, v => v.NavigateButton)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class NavigableViewModel : ReactiveObject, IRoutableViewModel
|
||||
{
|
||||
public NavigableViewModel(IScreen hostScreen = null)
|
||||
{
|
||||
HostScreen = hostScreen ?? Locator.Current.GetService<IScreen>();
|
||||
NavigateToDummyPage = ReactiveCommand.CreateFromObservable(
|
||||
() => HostScreen.Router.Navigate.Execute(new DummyViewModel()).Select(_ => Unit.Default));
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> NavigateToDummyPage { get; }
|
||||
|
||||
public string UrlPathSegment => "Navigable Page";
|
||||
|
||||
public IScreen HostScreen { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?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"
|
||||
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
|
||||
xmlns:local="clr-namespace:MasterDetail"
|
||||
x:Class="MasterDetail.NumberStreamPage"
|
||||
x:TypeArguments="local:NumberStreamViewModel">
|
||||
<ContentPage.Content>
|
||||
<StackLayout>
|
||||
<Label
|
||||
x:Name="NumberLabel"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
HorizontalOptions="CenterAndExpand"
|
||||
FontSize="36" />
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
</rxui:ReactiveContentPage>
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class NumberStreamPage : ReactiveContentPage<NumberStreamViewModel>
|
||||
{
|
||||
public NumberStreamPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.WhenActivated(
|
||||
disposables =>
|
||||
{
|
||||
this
|
||||
.ViewModel
|
||||
.NumberStream
|
||||
.ObserveOn(RxApp.MainThreadScheduler)
|
||||
.BindTo(this, x => x.NumberLabel.Text)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class NumberStreamViewModel : ReactiveObject, IRoutableViewModel
|
||||
{
|
||||
public NumberStreamViewModel()
|
||||
{
|
||||
NumberStream = Observable
|
||||
.Interval(TimeSpan.FromSeconds(1))
|
||||
.Select(x => x.ToString());
|
||||
}
|
||||
|
||||
public IObservable<string> NumberStream { get; }
|
||||
|
||||
public string UrlPathSegment => "Number Stream Page";
|
||||
|
||||
public IScreen HostScreen { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?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"
|
||||
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
|
||||
xmlns:local="clr-namespace:MasterDetail"
|
||||
x:Class="MasterDetail.DummyPage"
|
||||
x:TypeArguments="local:DummyViewModel">
|
||||
<ContentPage.Content>
|
||||
<StackLayout Spacing="10">
|
||||
<Button
|
||||
x:Name="NavigateButton"
|
||||
Text="Navigate"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
HorizontalOptions="Center" />
|
||||
<Button
|
||||
x:Name="BackButton"
|
||||
Text="Navigate Back"
|
||||
VerticalOptions="CenterAndExpand"
|
||||
HorizontalOptions="Center" />
|
||||
</StackLayout>
|
||||
</ContentPage.Content>
|
||||
</rxui:ReactiveContentPage>
|
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class DummyPage : ReactiveContentPage<DummyViewModel>
|
||||
{
|
||||
public DummyPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
this.WhenActivated(
|
||||
disposables =>
|
||||
{
|
||||
this
|
||||
.BindCommand(ViewModel, vm => vm.NavigateToDummyPage, v => v.NavigateButton)
|
||||
.DisposeWith(disposables);
|
||||
this
|
||||
.BindCommand(ViewModel, vm => vm.NavigateBack, v => v.BackButton)
|
||||
.DisposeWith(disposables);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class DummyViewModel : ReactiveObject, IRoutableViewModel
|
||||
{
|
||||
public DummyViewModel(IScreen hostScreen = null)
|
||||
{
|
||||
HostScreen = hostScreen ?? Locator.Current.GetService<IScreen>();
|
||||
|
||||
NavigateToDummyPage = ReactiveCommand.CreateFromObservable(
|
||||
() => HostScreen.Router.Navigate.Execute(new DummyViewModel()).Select(_ => Unit.Default));
|
||||
}
|
||||
|
||||
public ReactiveCommand<Unit, Unit> NavigateToDummyPage { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> NavigateBack => HostScreen.Router.NavigateBack;
|
||||
|
||||
public string UrlPathSegment => "Dummy Page";
|
||||
|
||||
public IScreen HostScreen { get; }
|
||||
}
|
||||
}
|
|
@ -4,8 +4,7 @@
|
|||
xmlns:rxui="clr-namespace:ReactiveUI.XamForms;assembly=ReactiveUI.XamForms"
|
||||
xmlns:local="clr-namespace:MasterDetail"
|
||||
x:Class="MasterDetail.MainPage"
|
||||
x:TypeArguments="local:MainViewModel"
|
||||
NavigationPage.HasNavigationBar="False">
|
||||
x:TypeArguments="local:MainViewModel">
|
||||
|
||||
<MasterDetailPage.Master>
|
||||
<ContentPage Title="Master" Padding="0,40,0,0" Icon="hamburger.png">
|
||||
|
@ -13,7 +12,7 @@
|
|||
<ListView x:Name="MyListView" SeparatorVisibility="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:CustomCell />
|
||||
<local:MasterCell />
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
|
@ -21,4 +20,8 @@
|
|||
</ContentPage>
|
||||
</MasterDetailPage.Master>
|
||||
|
||||
<MasterDetailPage.Detail>
|
||||
<rxui:RoutedViewHost x:Name="ViewHost" />
|
||||
</MasterDetailPage.Detail>
|
||||
|
||||
</rxui:ReactiveMasterDetailPage>
|
||||
|
|
|
@ -4,24 +4,22 @@ using System.Reactive.Disposables;
|
|||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public partial class MainPage : ReactiveMasterDetailPage<MainViewModel>
|
||||
{
|
||||
public MainPage()
|
||||
public MainPage(MainViewModel viewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
ViewModel = viewModel;
|
||||
|
||||
ViewModel = new MainViewModel();
|
||||
Detail = new NavigationPage(new MyDetailPage(ViewModel.Detail));
|
||||
InitializeComponent();
|
||||
|
||||
this.WhenActivated(
|
||||
disposables =>
|
||||
{
|
||||
this
|
||||
.OneWayBind(ViewModel, vm => vm.MyList, v => v.MyListView.ItemsSource)
|
||||
.OneWayBind(ViewModel, vm => vm.MenuItems, v => v.MyListView.ItemsSource)
|
||||
.DisposeWith(disposables);
|
||||
this
|
||||
.Bind(ViewModel, vm => vm.Selected, v => v.MyListView.SelectedItem)
|
||||
|
@ -30,9 +28,11 @@ namespace MasterDetail
|
|||
.WhenAnyValue(x => x.ViewModel.Selected)
|
||||
.Where(x => x != null)
|
||||
.Subscribe(
|
||||
model =>
|
||||
_ =>
|
||||
{
|
||||
// Deselect the cell.
|
||||
MyListView.SelectedItem = null;
|
||||
// Hide the master panel.
|
||||
IsPresented = false;
|
||||
})
|
||||
.DisposeWith(disposables);
|
||||
|
|
|
@ -1,45 +1,52 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using Splat;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class MainViewModel : ReactiveObject
|
||||
public class MainViewModel : ReactiveObject, IScreen
|
||||
{
|
||||
public MainViewModel()
|
||||
{
|
||||
var cellVms = GetData().Select(model => new CustomCellViewModel(model));
|
||||
MyList = new ObservableCollection<CustomCellViewModel>(cellVms);
|
||||
Router = new RoutingState();
|
||||
Locator.CurrentMutable.RegisterConstant(this, typeof(IScreen));
|
||||
|
||||
Detail = new MyDetailViewModel();
|
||||
Detail.Model = cellVms.First().Model;
|
||||
MenuItems = GetMenuItems();
|
||||
|
||||
NavigateToMenuItem = ReactiveCommand.CreateFromObservable<IRoutableViewModel, Unit>(
|
||||
routableVm => Router.NavigateAndReset.Execute(routableVm).Select(_ => Unit.Default));
|
||||
|
||||
this.WhenAnyValue(x => x.Selected)
|
||||
.Where(x => x != null)
|
||||
.Subscribe(cellVm => Detail.Model = cellVm.Model);
|
||||
.StartWith(MenuItems.First())
|
||||
.Select(x => Locator.Current.GetService<IRoutableViewModel>(x.TargetType.FullName))
|
||||
.InvokeCommand(NavigateToMenuItem);
|
||||
}
|
||||
|
||||
private CustomCellViewModel _selected;
|
||||
public CustomCellViewModel Selected
|
||||
private MasterCellViewModel _selected;
|
||||
public MasterCellViewModel Selected
|
||||
{
|
||||
get => _selected;
|
||||
set => this.RaiseAndSetIfChanged(ref _selected, value);
|
||||
}
|
||||
|
||||
public MyDetailViewModel Detail { get; }
|
||||
public ReactiveCommand<IRoutableViewModel, Unit> NavigateToMenuItem { get; }
|
||||
|
||||
public ObservableCollection<CustomCellViewModel> MyList { get; }
|
||||
public IEnumerable<MasterCellViewModel> MenuItems { get; }
|
||||
|
||||
private IEnumerable<MyModel> GetData()
|
||||
public RoutingState Router { get; }
|
||||
|
||||
private IEnumerable<MasterCellViewModel> GetMenuItems()
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new MyModel { Title = "Contacts", IconSource = "contacts.png" },
|
||||
new MyModel { Title = "Reminders", IconSource = "reminders.png" },
|
||||
new MyModel { Title = "TodoList", IconSource = "todo.png" },
|
||||
new MasterCellViewModel { Title = "Navigable Page", IconSource = "contacts.png", TargetType = typeof(NavigableViewModel) },
|
||||
new MasterCellViewModel { Title = "Number Stream Page", IconSource = "reminders.png", TargetType = typeof(NumberStreamViewModel) },
|
||||
new MasterCellViewModel { Title = "Letter Stream Page", IconSource = "todo.png", TargetType = typeof(LetterStreamViewModel) },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rxui:ReactiveViewCell 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:MasterDetail"
|
||||
x:Class="MasterDetail.MasterCell"
|
||||
x:TypeArguments="local:MasterCellViewModel">
|
||||
|
||||
<Grid Padding="5,10">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image x:Name="IconImage" />
|
||||
<Label x:Name="TitleLabel" Grid.Column="1" />
|
||||
</Grid>
|
||||
|
||||
</rxui:ReactiveViewCell>
|
|
@ -0,0 +1,35 @@
|
|||
using System;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public partial class MasterCell : ReactiveViewCell<MasterCellViewModel>
|
||||
{
|
||||
public MasterCell()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Disposal of this subsciption is *not* necessary because we're simply monitoring
|
||||
// the property (ViewModel) on the view itself, so the subscription is attaching to
|
||||
// PropertyChanged on this view. This means the view has a reference to itself and
|
||||
// thus, doesn't prevent the it from being garbage collected.
|
||||
// Note: WPF & UWP *do* require disposal in this scenario, thanks to the way
|
||||
// dependency properties work.
|
||||
this.WhenAnyValue(x => x.ViewModel)
|
||||
.Where(x => x != null)
|
||||
.Do(PopulateFromViewModel)
|
||||
.Subscribe();
|
||||
}
|
||||
|
||||
private void PopulateFromViewModel(MasterCellViewModel viewModel)
|
||||
{
|
||||
// Because menu items usually don't change for the lifetime of an app (for most use cases),
|
||||
// set the values directly instead of binding, for better performance.
|
||||
// If your ViewModel properties don't change over time, definitely use this pattern.
|
||||
TitleLabel.Text = viewModel.Title;
|
||||
IconImage.Source = viewModel.IconSource;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class MasterCellViewModel
|
||||
{
|
||||
public string Title { get; set; }
|
||||
|
||||
public string IconSource { get; set; }
|
||||
|
||||
public Type TargetType { get; set; }
|
||||
}
|
||||
}
|
|
@ -4,29 +4,9 @@
|
|||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="DynamicMasterDetail\**" />
|
||||
<EmbeddedResource Remove="DynamicMasterDetail\**" />
|
||||
<None Remove="DynamicMasterDetail\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ReactiveUI" Version="*" />
|
||||
<PackageReference Include="ReactiveUI.XamForms" Version="*" />
|
||||
<PackageReference Include="ReactiveUI" Version="8.7.2" />
|
||||
<PackageReference Include="ReactiveUI.XamForms" Version="8.7.2" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="3.1.0.697729" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="CustomCell.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="MyDetailPage.xaml">
|
||||
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,36 +0,0 @@
|
|||
using System;
|
||||
using System.Reactive.Disposables;
|
||||
using System.Reactive.Linq;
|
||||
using ReactiveUI;
|
||||
using ReactiveUI.XamForms;
|
||||
using Xamarin.Forms.Xaml;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
[XamlCompilation(XamlCompilationOptions.Compile)]
|
||||
public partial class MyDetailPage : ReactiveContentPage<MyDetailViewModel>
|
||||
{
|
||||
public MyDetailPage(MyDetailViewModel 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
using ReactiveUI;
|
||||
|
||||
namespace MasterDetail
|
||||
{
|
||||
public class MyDetailViewModel : ReactiveObject
|
||||
{
|
||||
private MyModel _model;
|
||||
|
||||
public MyModel Model
|
||||
{
|
||||
get => _model;
|
||||
set => this.RaiseAndSetIfChanged(ref _model, value);
|
||||
}
|
||||
|
||||
public string Title => Model.Title;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
namespace MasterDetail
|
||||
{
|
||||
public class MyModel
|
||||
{
|
||||
public string Title { get; set; }
|
||||
|
||||
public string IconSource { get; set; }
|
||||
}
|
||||
}
|
|
@ -148,7 +148,7 @@
|
|||
<Reference Include="Xamarin.iOS" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ReactiveUI" Version="*" />
|
||||
<PackageReference Include="ReactiveUI" Version="8.7.2" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="3.1.0.697729" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче