diff --git a/samples/xamarin-forms/MasterDetail/Android/MasterDetail.Android.csproj b/samples/xamarin-forms/MasterDetail/Android/MasterDetail.Android.csproj
index 5f3c5c484f..71333c98ce 100644
--- a/samples/xamarin-forms/MasterDetail/Android/MasterDetail.Android.csproj
+++ b/samples/xamarin-forms/MasterDetail/Android/MasterDetail.Android.csproj
@@ -49,7 +49,7 @@
-
+
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/App.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/App.xaml.cs
index 25e43a7755..024762c4b3 100644
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/App.xaml.cs
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/App.xaml.cs
@@ -13,9 +13,9 @@ public App()
{
InitializeComponent();
- Locator.CurrentMutable.Register(() => new CustomCell(), typeof(IViewFor));
+ var bootstrapper = new AppBootstrapper();
- MainPage = new MainPage();
+ MainPage = new MainPage(bootstrapper.CreateMainViewModel());
}
protected override void OnStart()
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/AppBootstrapper.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/AppBootstrapper.cs
new file mode 100644
index 0000000000..b0b1c81ba3
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/AppBootstrapper.cs
@@ -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));
+ Locator.CurrentMutable.Register(() => new MasterCell(), typeof(IViewFor));
+
+ // Detail pages
+ Locator.CurrentMutable.Register(() => new NavigablePage(), typeof(IViewFor));
+ Locator.CurrentMutable.Register(() => new NumberStreamPage(), typeof(IViewFor));
+ Locator.CurrentMutable.Register(() => new LetterStreamPage(), typeof(IViewFor));
+ }
+
+ 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);
+ }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCell.xaml b/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCell.xaml
deleted file mode 100644
index 1211d4d5fe..0000000000
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCell.xaml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCell.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCell.xaml.cs
deleted file mode 100644
index b9063db795..0000000000
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCell.xaml.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using System.Reactive.Disposables;
-using ReactiveUI;
-using ReactiveUI.XamForms;
-using Xamarin.Forms;
-
-namespace MasterDetail
-{
- public partial class CustomCell : ReactiveViewCell
- {
- 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);
- });
- }
- }
-}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCellViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCellViewModel.cs
deleted file mode 100644
index 973e9d96e4..0000000000
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/CustomCellViewModel.cs
+++ /dev/null
@@ -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;
- }
-}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamPage.xaml b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamPage.xaml
new file mode 100644
index 0000000000..8f1bc3cf47
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamPage.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamPage.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamPage.xaml.cs
new file mode 100644
index 0000000000..ac6096ce2f
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamPage.xaml.cs
@@ -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
+ {
+ public LetterStreamPage()
+ {
+ InitializeComponent();
+
+ this.WhenActivated(
+ disposables =>
+ {
+ this
+ .OneWayBind(ViewModel, vm => vm.CurrentLetter, v => v.LetterLabel.Text)
+ .DisposeWith(disposables);
+ });
+ }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamViewModel.cs
new file mode 100644
index 0000000000..01f551bc79
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/LetterStreamViewModel.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Reactive.Linq;
+using ReactiveUI;
+
+namespace MasterDetail
+{
+ public class LetterStreamViewModel : ReactiveObject, IRoutableViewModel
+ {
+ private ObservableAsPropertyHelper _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; }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailPage.xaml b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigablePage.xaml
similarity index 73%
rename from samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailPage.xaml
rename to samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigablePage.xaml
index deafee092e..2993492b6b 100644
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailPage.xaml
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigablePage.xaml
@@ -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">
-
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigablePage.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigablePage.xaml.cs
new file mode 100644
index 0000000000..b2cc9375bd
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigablePage.xaml.cs
@@ -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
+ {
+ public NavigablePage()
+ {
+ InitializeComponent();
+
+ this.WhenActivated(
+ disposables =>
+ {
+ this
+ .BindCommand(ViewModel, vm => vm.NavigateToDummyPage, v => v.NavigateButton)
+ .DisposeWith(disposables);
+ });
+ }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigableViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigableViewModel.cs
new file mode 100644
index 0000000000..eb56b2397b
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NavigableViewModel.cs
@@ -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();
+ NavigateToDummyPage = ReactiveCommand.CreateFromObservable(
+ () => HostScreen.Router.Navigate.Execute(new DummyViewModel()).Select(_ => Unit.Default));
+ }
+
+ public ReactiveCommand NavigateToDummyPage { get; }
+
+ public string UrlPathSegment => "Navigable Page";
+
+ public IScreen HostScreen { get; }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamPage.xaml b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamPage.xaml
new file mode 100644
index 0000000000..efef883ed2
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamPage.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamPage.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamPage.xaml.cs
new file mode 100644
index 0000000000..b279ba28be
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamPage.xaml.cs
@@ -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
+ {
+ public NumberStreamPage()
+ {
+ InitializeComponent();
+
+ this.WhenActivated(
+ disposables =>
+ {
+ this
+ .ViewModel
+ .NumberStream
+ .ObserveOn(RxApp.MainThreadScheduler)
+ .BindTo(this, x => x.NumberLabel.Text)
+ .DisposeWith(disposables);
+ });
+ }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamViewModel.cs
new file mode 100644
index 0000000000..67e3f406cf
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DetailPages/NumberStreamViewModel.cs
@@ -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 NumberStream { get; }
+
+ public string UrlPathSegment => "Number Stream Page";
+
+ public IScreen HostScreen { get; }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DummyPage.xaml b/samples/xamarin-forms/MasterDetail/MasterDetail/DummyPage.xaml
new file mode 100644
index 0000000000..05f6528979
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DummyPage.xaml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DummyPage.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DummyPage.xaml.cs
new file mode 100644
index 0000000000..26e1d89912
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DummyPage.xaml.cs
@@ -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
+ {
+ 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);
+ });
+ }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/DummyViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/DummyViewModel.cs
new file mode 100644
index 0000000000..fec205ccae
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/DummyViewModel.cs
@@ -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();
+
+ NavigateToDummyPage = ReactiveCommand.CreateFromObservable(
+ () => HostScreen.Router.Navigate.Execute(new DummyViewModel()).Select(_ => Unit.Default));
+ }
+
+ public ReactiveCommand NavigateToDummyPage { get; }
+
+ public ReactiveCommand NavigateBack => HostScreen.Router.NavigateBack;
+
+ public string UrlPathSegment => "Dummy Page";
+
+ public IScreen HostScreen { get; }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml b/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml
index 3b8b40672f..dd0402808c 100644
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml
@@ -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">
@@ -13,7 +12,7 @@
-
+
@@ -21,4 +20,8 @@
+
+
+
+
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml.cs
index a2eb0cbfd2..3e7206a071 100644
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml.cs
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/MainPage.xaml.cs
@@ -4,24 +4,22 @@
using System.Reactive.Linq;
using ReactiveUI;
using ReactiveUI.XamForms;
-using Xamarin.Forms;
namespace MasterDetail
{
public partial class MainPage : ReactiveMasterDetailPage
{
- 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 @@ public MainPage()
.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);
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MainViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/MainViewModel.cs
index d0519190ed..35aba32582 100644
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MainViewModel.cs
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/MainViewModel.cs
@@ -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(cellVms);
+ Router = new RoutingState();
+ Locator.CurrentMutable.RegisterConstant(this, typeof(IScreen));
- Detail = new MyDetailViewModel();
- Detail.Model = cellVms.First().Model;
+ MenuItems = GetMenuItems();
+
+ NavigateToMenuItem = ReactiveCommand.CreateFromObservable(
+ 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(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 NavigateToMenuItem { get; }
+
+ public IEnumerable MenuItems { get; }
- public ObservableCollection MyList { get; }
+ public RoutingState Router { get; }
- private IEnumerable GetData()
+ private IEnumerable 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) },
};
}
}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCell.xaml b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCell.xaml
new file mode 100644
index 0000000000..21fe510dbd
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCell.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCell.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCell.xaml.cs
new file mode 100644
index 0000000000..3c90fcd20d
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCell.xaml.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Reactive.Linq;
+using ReactiveUI;
+using ReactiveUI.XamForms;
+
+namespace MasterDetail
+{
+ public partial class MasterCell : ReactiveViewCell
+ {
+ 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;
+ }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCellViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCellViewModel.cs
new file mode 100644
index 0000000000..6262ec8912
--- /dev/null
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterCellViewModel.cs
@@ -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; }
+ }
+}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MasterDetail.csproj b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterDetail.csproj
index 315ba2a937..f095acd03d 100644
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MasterDetail.csproj
+++ b/samples/xamarin-forms/MasterDetail/MasterDetail/MasterDetail.csproj
@@ -4,29 +4,9 @@
netstandard2.0
-
- pdbonly
- true
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
- MSBuild:Compile
-
-
- MSBuild:UpdateDesignTimeXaml
-
-
\ No newline at end of file
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailPage.xaml.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailPage.xaml.cs
deleted file mode 100644
index b2fe27388f..0000000000
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailPage.xaml.cs
+++ /dev/null
@@ -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
- {
- 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;
- }
- }
-}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailViewModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailViewModel.cs
deleted file mode 100644
index 5c03b7ff5c..0000000000
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MyDetailViewModel.cs
+++ /dev/null
@@ -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;
- }
-}
diff --git a/samples/xamarin-forms/MasterDetail/MasterDetail/MyModel.cs b/samples/xamarin-forms/MasterDetail/MasterDetail/MyModel.cs
deleted file mode 100644
index 770e3cf5fa..0000000000
--- a/samples/xamarin-forms/MasterDetail/MasterDetail/MyModel.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MasterDetail
-{
- public class MyModel
- {
- public string Title { get; set; }
-
- public string IconSource { get; set; }
- }
-}
diff --git a/samples/xamarin-forms/MasterDetail/iOS/MasterDetail.iOS.csproj b/samples/xamarin-forms/MasterDetail/iOS/MasterDetail.iOS.csproj
index dc181266b7..81788e556e 100644
--- a/samples/xamarin-forms/MasterDetail/iOS/MasterDetail.iOS.csproj
+++ b/samples/xamarin-forms/MasterDetail/iOS/MasterDetail.iOS.csproj
@@ -148,7 +148,7 @@
-
+