Reputation: 949
We are successfully wiring ContentViews to their ViewModels when the ContentView is directly contained in a Page, using XAML like:
<local:AwesomeView mvvm:ViewModelLocator.AutowirePartialView=”{x:Reference self}” />
Where self is the parent page.
However, we have ContentViews which contain ContentViews, and using AutoWirePartialView as above for the nested view does not work. The ContentViews don't get wired up to their ViewModels.
Looking at the Prism code:
So it's fairly clear from the Prism code why this won't work!
Is there a way to achieve this with Prism?
Versions: Xamarin.Forms - 4.4.0.991265
Prism - 7.1.0.431
Upvotes: 1
Views: 911
Reputation: 5799
Partial Views are indeed being made Obsolete. The reason it's obsolete is that we are bringing Region support to Prism 8. Partial Views were always meant as a quick and temporary solution to help bridge the gap until we got to implementing Regions for Prism.Forms. Regions are far more powerful and will let you do a lot more like nesting, and lazily loading views.
Realistically the concept of nested Regions where you have:
ComponentViewA which has its own ViewModel.
Then you have ComponentViewB which has it's own ViewModel and has ComponentViewA as a child
And ComponentViewA is itself a child of AwesomePage
It sounds like this is the general concept that you're looking to support. So the short answer is that in Prism 7 there is no good way of doing this. There are certainly some hacks like you could for instance pass the page as a parameter and set the property in the code behind like:
public class ComponentViewB : ContentView
{
public static readonly BindableProperty ParentPageProperty =
BindableProperty.Create(nameof(ParentPage), typeof(Page), typeof(ComponentViewB), null, propertyChanged: OnParentPageChanged);
private static void OnParentPageChanged(BindableObject bindable, object oldValue, object newValue)
{
// This guards the action from being performed more than once.
if(oldValue is null && newValue != null && bindable is ComponentViewB view)
{
// This assumes you've set the property x:Name="componentViewA"
// for your ComponentViewA in XAML
ViewModelLocator.SetAutowirePartialView(view.componentViewA, (view.ParentPage);
}
}
public Page ParentPage
{
get => (Page)GetValue(ParentPageProperty);
set => SetValue(ParentPageProperty, value);
}
}
To be honest if I had to make something work today this is how I would recommend doing it. Once we merge the PR I referenced above I would suggest that you update to the previews and migrate to use Regions.
Upvotes: 2
Reputation: 119
My opinion, in order to keep things clean, is to use the default prism approach and bind a viewmodel to the respective page only and not to any subviews.
Any binding to a component inside the page should be made through properties on the page's viewmodel so you can achieve bindings no matter the depth of the content view in the display hierarchy.
For instance:
Your page's view model
public class PageAViewModel : ViewModelBase
{
public ContentViewAViewModel ContentViewViewModel
{
get { return _contentViewViewModel; }
set { SetProperty(ref _contentViewViewModel, value); }
}
}
Your page's view
<?xml version="1.0" encoding="UTF-8"?>
<views:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:components="clr-YourProject.Components"
xmlns:views="clr-YourProject.Views"
x:Class="YourProject.Views.PageA">
<Grid RowSpacing="0">
<Grid RowSpacing="0">
<components:ContentViewA BindingContext="{Binding ContentViewViewModel}"/>
</Grid>
</Grid>
</views:BasePage>
Upvotes: 1