David Pugh
David Pugh

Reputation: 992

Can't bind property for ContentView control

I have a ContentView called HomePageOrientationViewLoader that I want to use in a ContentPage called HomePage. HomePageOrientationViewLoader will either load a ContentView called HomePageLandscape if the orientation is in Landscape or a ContentView called HomePagePortrait if the orientation is in Portrait.

I am doing this so that I can load a different layout for landscape vs portrait so I can optimize my layout.

My issue is that I use dependency injection to inject my ViewModel HomeViewModel. I inject this into the ContentPage HomePage and I am attempting to pass the HomeViewModel from the ContentPage HomePage's XAML into the markup for HomePageOrientationViewLoader.

Here is my HomePage.xaml.cs code behind for my ContentPage:

using ScoreKeepersBoard.ViewModels;
namespace ScoreKeepersBoard.Views;

public partial class HomePage : ContentPage
{

    public HomePage(HomeViewModel homeViewModelInj)
    {
        HomeViewModel = homeViewModelInj;
        BindingContext = homeViewModelInj;
        InitializeComponent();
    }

    HomeViewModel HomeViewModel { get; set; }

    protected override void OnNavigatedTo(NavigatedToEventArgs args)
    {
        ((HomeViewModel)BindingContext).CreateInitialGameTypes();
        base.OnNavigatedTo(args);
    }
}

Here is my HomePage.xaml for the ContentPage:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
             x:Class="ScoreKeepersBoard.Views.HomePage"
             Title="HomePage">
    <VerticalStackLayout>

       <controls:HomePageOrientationViewLoader
            BindingContext="{Binding HomeViewModel}"
           >
       </controls:HomePageOrientationViewLoader>
        
    </VerticalStackLayout>
</ContentPage>

Here is my HomePageOrientationViewLoader.xaml.cs code behind for my ContentView:

using System.Reflection;
using ScoreKeepersBoard.ViewModels;


namespace ScoreKeepersBoard.ContentViews;

public partial class HomePageOrientationViewLoader : ContentView
{

    public ContentView homePagePortraitContentView;
    public ContentView homePageLandscapeContentView;

    public HomeViewModel HomeViewModel { get; set; }

    public HomePageOrientationViewLoader()
    {
        InitializeComponent();

        try
        {
             //homeVM is always null
             HomeViewModel homeVM = ((HomeViewModel)BindingContext);
             string entryValue = homeVM.EntryValue;

            homePagePortraitContentView = new HomePagePortrait(homeVM);
            homePageLandscapeContentView = new HomePageLandscape(homeVM);

        }
        catch (Exception e)
        {
            string message = e.Message;
        }

        DeviceDisplay.Current.MainDisplayInfoChanged += Current_MainDisplayInfoChanged;
        this.Content = DeviceDisplay.Current.MainDisplayInfo.Orientation == DisplayOrientation.Portrait ? homePagePortraitContentView : homePageLandscapeContentView;

    }

    private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
    {
        if (e.DisplayInfo.Orientation == DisplayOrientation.Landscape)
        {
                this.Content = homePageLandscapeContentView;
        }
        else if (e.DisplayInfo.Orientation == DisplayOrientation.Portrait)
        {
            this.Content = homePagePortraitContentView;
        }
        else
        {
            this.Content = homePagePortraitContentView;
        }

    }
}

The code compiles and runs, but the issue is that the BindingContext on HomePageOrientationViewLoader is always null. I would expect this to be set from the property defined in the ContentView's markup in HomePage ContentPage.

I also tried to set HomePageOrientationViewLoader's markup in ContentView's markup in HomePage ContentPage as just a normal Property defined on HomePageOrientationViewLoader as such:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
             x:Class="ScoreKeepersBoard.Views.HomePage"
             Title="HomePage">
    <VerticalStackLayout>

       <controls:HomePageOrientationViewLoader
            HomeViewModel = "{Binding HomeViewModel}">
       </controls:HomePageOrientationViewLoader>
        
    </VerticalStackLayout>
</ContentPage>

But this won't even compile. I get the following error:

/Users/RemoteCommand/Projects/ScoreKeepersBoard/Views/HomePage.xaml(13,13): Error XFC0009: No property, BindableProperty, or event found for "HomeViewModel", or mismatching type between value and property. (XFC0009)

This is obviously not true since HomeViewModel is a Property on both HomePage ContentPage and HomePageOrientationViewLoader ContentView.

I need to be able to have HomeViewModel in HomePageOrientationViewLoader and then to pass this on to HomePageLandscape and HomePagePortrait ContentViews so they can share the same ViewModel when the user switches between Portrait and Landscape view but I am so far not able to get this working. I would appreciate any help.

UPDATE Jason commented: " If you want the control to have the same BindingContext as the page, you don't need to do anything - that should automatically inherit." I just tried to remove the binding property on the control markup as such:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:ScoreKeepersBoard.ContentViews"
             x:Class="ScoreKeepersBoard.Views.HomePage"
             Title="HomePage">
    <VerticalStackLayout>

       <controls:HomePageOrientationViewLoader>

       </controls:HomePageOrientationViewLoader>
        
    </VerticalStackLayout>
</ContentPage>

And when my HomePageOrientationViewLoader page loads the constructor the HomeViewModel from HomePageOrientationViewLoader's BindingContext is still null:

enter image description here

UPDATE 2 Jason said that you should not assume the BindingContext is set in the constructor. He was right. When my HomePageOrientationViewLoader ContentView first loads the ContentView is not set but when I change the orientation and check to see what the BindingContext is as such:

private void Current_MainDisplayInfoChanged(object sender, DisplayInfoChangedEventArgs e)
{

    HomeViewModel homeVM = ((HomeViewModel)BindingContext);
    homePageLandscapeContentView.BindingContext = homeVM;
    homePagePortraitContentView.BindingContext = homeVM;

   ...
}

homeVM which is set from the BindingContext of HomePageOrientationViewLoader is no longer null. I can then set the BindingContext of HomePageLandscape and HomePagePortrait and their ViewModels are active and bound. The only issue for me is that when the page initially loads HomePageLandscape and HomePagePortrait don't have their BindingContext set.
How can I get around this? Is there some event on ContentView that gets triggered when BindingContext is set so I could then override that event and bind HomePageLandscape and HomePagePortrait's Binding Context to the BindingContext of HomePageOrientationViewLoader?

Upvotes: 0

Views: 222

Answers (1)

Jason
Jason

Reputation: 89082

ContentView has an OnBindingContextChanged method you can override

Upvotes: 0

Related Questions