Reputation: 647
I'm new to MvvmCross (I'm using Xamarin.Forms) and navigating between MvxContentPages is easy.
But I'd like to navigate between ContentView embedded in a page but can't find any documentation that references using MvxContentView.
Consider the following Page
<ContentPage.Content>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0">
<Button Text="Content 1" Command="{Binding GotoContent1}"/>
<Button Text="Content 2" Command="{Binding GotoContent2}"/>
<Button Text="Content 3" Command="{Binding GotoContent3}"/>
<Button Text="Content 4" Command="{Binding GotoContent4}"/>
</StackLayout>
<ContentView x:Name="ContentContainer" Grid.Column="1"
HorizontalOptions="Fill"
VerticalOptions="Fill"/>
</Grid>
</ContentPage.Content>
When the user clicks one of the buttons, I'd like to display a MvxContentView in the ContentContainer (or if there is an alternative way to do it, that's fine, I just don't want to make 4 pages that are essentially the same but have different content in the content view placeholder).
Upvotes: 2
Views: 958
Reputation: 647
I wrote a wrapper over a ContentPage and ContentView to enable ViewModel first navigation. It's quite simple, you put a MvxNestableContentView inside of a MvxNestableContentPage and give it a name. Then in your ViewModel (which is derived from MvxNestableViewModel) you simple call SetContent(typeof(viewmodel));
public interface INestedViewModelEventArgs
{
string ContainerName { get; set; }
IMvxNestableViewModel ViewModel { get; set; }
bool SuppressAppearing { get; set; }
}
public interface IMvxNestableViewModel : IMvxViewModel
{
event EventHandler SetNestedViewModel;
IMvxNestableViewModel SetContent(Type tViewModel, string containerName, bool suppressAppearing = false);
}
public interface IMvxNestableContentView
{
void OnAppearing();
void OnDisappearing();
}
public interface IMvxNestableContentPage
{
}
public class MvxNestableContentView : MvxContentView, IMvxNestableContentView
{
public MvxNestableContentView()
{
}
protected override void OnViewModelSet()
{
if (ViewModel is IMvxNestableViewModel vm)
{
vm.SetNestedViewModel += VmOnSetNestedViewModel;
}
ViewModel?.ViewCreated();
}
public virtual void OnAppearing()
{
ViewModel?.ViewAppearing();
ViewModel?.ViewAppeared();
foreach (var v in _nestedContentViews)
{
v.OnAppearing();
}
}
public virtual void OnDisappearing()
{
if (ViewModel is IMvxNestableViewModel vm)
{
vm.SetNestedViewModel -= VmOnSetNestedViewModel;
}
foreach (var v in _nestedContentViews)
{
v.OnDisappearing();
}
ViewModel?.ViewDisappearing();
ViewModel?.ViewDisappeared();
ViewModel?.ViewDestroy();
}
private readonly List<MvxNestableContentView> _nestedContentViews = new List<MvxNestableContentView>();
private void VmOnSetNestedViewModel(object sender, EventArgs e)
{
if (!(e is NestedViewModelEventArgs args)) return;
if (string.IsNullOrWhiteSpace(args.ContainerName) || args.ViewModel == null) return;
var contentView = this.FindByName<MvxNestableContentView>(args.ContainerName);
if (contentView == null)
{
throw new Exception("MvxNestableContentView : MvxNestableContentView named " + args.ContainerName + " not found");
}
var viewLookup = Mvx.IoCProvider.Resolve<IMvxViewsContainer>();
var viewType = viewLookup.GetViewType(args.ViewModel.GetType());
var viewObject = Mvx.IoCProvider.IoCConstruct(viewType);
if (!(viewObject is MvxNestableContentView view))
{
throw new Exception("MvxNestableContentView : view is not MvxNestableContentView");
}
view.ViewModel = args.ViewModel;
var existingContent = contentView.Content as MvxNestableContentView;
if (!args.SuppressAppearing) view.OnAppearing();
_nestedContentViews.Add(view);
contentView.Content = view;
if (existingContent == null) return;
existingContent.OnDisappearing();
_nestedContentViews.Remove(existingContent);
}
}
public class MvxNestableContentView<TViewModel> : MvxNestableContentView, IMvxElement<TViewModel>
where TViewModel : class, IMvxViewModel
{
public new TViewModel ViewModel
{
get => (TViewModel)base.ViewModel;
set => base.ViewModel = value;
}
}
public class MvxNestableContentPage : MvxContentPage, IMvxNestableContentPage
{
public MvxNestableContentPage()
{
}
protected override void OnViewModelSet()
{
if (ViewModel is IMvxNestableViewModel vm)
{
vm.SetNestedViewModel += VmOnSetNestedViewModel;
}
base.OnViewModelSet();
}
protected override void OnAppearing()
{
base.OnAppearing();
foreach (var v in _nestedContentViews)
{
v.OnAppearing();
}
}
protected override void OnDisappearing()
{
if (ViewModel is IMvxNestableViewModel vm)
{
vm.SetNestedViewModel -= VmOnSetNestedViewModel;
}
foreach (var v in _nestedContentViews)
{
v.OnDisappearing();
}
base.OnDisappearing();
}
private readonly List<MvxNestableContentView> _nestedContentViews = new List<MvxNestableContentView>();
private void VmOnSetNestedViewModel(object sender, EventArgs e)
{
if (!(e is NestedViewModelEventArgs args)) return;
if (string.IsNullOrWhiteSpace(args.ContainerName) || args.ViewModel == null) return;
var contentView = this.FindByName<MvxNestableContentView>(args.ContainerName);
if (contentView == null)
{
throw new Exception("MvxNestableContentPage : MvxNestableContentView named " + args.ContainerName + " not found");
}
var viewLookup = Mvx.IoCProvider.Resolve<IMvxViewsContainer>();
var viewType = viewLookup.GetViewType(args.ViewModel.GetType());
var viewObject = Mvx.IoCProvider.IoCConstruct(viewType);
if (!(viewObject is MvxNestableContentView view))
{
throw new Exception("MvxNestableContentPage : view is not MvxNestableContentView");
}
view.ViewModel = args.ViewModel;
var existingContent = contentView.Content as MvxNestableContentView;
if (!args.SuppressAppearing) view.OnAppearing();
_nestedContentViews.Add(view);
contentView.Content = view;
if (existingContent == null) return;
existingContent.OnDisappearing();
_nestedContentViews.Remove(existingContent);
}
}
public class MvxNestableContentPage<TViewModel> : MvxNestableContentPage, IMvxPage<TViewModel>
where TViewModel : class, IMvxViewModel
{
public new TViewModel ViewModel
{
get => (TViewModel)base.ViewModel;
set => base.ViewModel = value;
}
}
public abstract class MvxNestableViewModel : MvxViewModel, IMvxNestableViewModel
{
public event EventHandler SetNestedViewModel;
public virtual IMvxNestableViewModel SetContent(Type viewModelType, string containerName, bool suppressAppearing = false)
{
if (!(Mvx.IoCProvider.IoCConstruct(viewModelType) is IMvxNestableViewModel viewModel))
{
return null;
}
viewModel.Start();
viewModel.Prepare();
viewModel.Initialize();
SetNestedViewModel?.Invoke(this, new NestedViewModelEventArgs
{
ContainerName = containerName,
ViewModel = viewModel,
SuppressAppearing = suppressAppearing
});
return viewModel;
}
}
public abstract class MvxNestableViewModel<TParameter> : MvxNestableViewModel, IMvxViewModel<TParameter>, IMvxNestableViewModel
{
public abstract void Prepare(TParameter parameter);
}
public abstract class MvxNestableViewModelResult<TResult> : MvxNestableViewModel, IMvxViewModelResult<TResult>, IMvxNestableViewModel
{
public TaskCompletionSource<object> CloseCompletionSource { get; set; }
public override void ViewDestroy(bool viewFinishing = true)
{
if (viewFinishing && CloseCompletionSource != null && !CloseCompletionSource.Task.IsCompleted && !CloseCompletionSource.Task.IsFaulted)
CloseCompletionSource?.TrySetCanceled();
base.ViewDestroy(viewFinishing);
}
}
public abstract class MvxNestableViewModel<TParameter, TResult> : MvxNestableViewModelResult<TResult>, IMvxViewModel<TParameter, TResult>, IMvxNestableViewModel
{
public abstract void Prepare(TParameter parameter);
}
public class NestedViewModelEventArgs : EventArgs, INestedViewModelEventArgs
{
public string ContainerName { get; set; }
public IMvxNestableViewModel ViewModel { get; set; }
public bool SuppressAppearing { get; set; }
}
Upvotes: 1
Reputation: 5099
The Content View is simply a Layout underneath, so just use 1 content view and just use the same existing ViewModel for all 4. And just change the value of the stuff inside the content view from the ViewModel depending on which button was clicked.
Upvotes: 0