XamarinForms ContentPresenter not passing viewmodel data to contentpage being displayed

90 views Asked by At

As one of a few different methods for displaying ads on a page, I tried creating a ContentPage that gets instantiated as the parent class for another page.

Here is how the parent page is created called AdFrame.Xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:services="clr-namespace:BoomStick.Services"
             x:Class="BoomStick.Views.AdFrame">

    <ContentPage.Content>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.1*" />
                <RowDefinition Height="0.9*" />
                <RowDefinition Height="0.1*" />
            </Grid.RowDefinitions>
            <Frame Grid.Row="0" BackgroundColor="Red"  HeightRequest="100" WidthRequest="200" />
            <ContentPresenter Grid.Row="1" />
            <StackLayout Grid.Row="2" BackgroundColor="Blue">
                <services:AdView />
            </StackLayout>
        </Grid>
    </ContentPage.Content>
</ContentPage>

AdFrame.xaml.cs

    public partial class AdFrame : ContentPage
    {
        public AdFrame()
        {
            InitializeComponent();
        }
    }

Then the page the is based on this parent.

Home.xaml

<d:AdFrame
    xmlns:d="clr-namespace:BoomStick.Views;assembly=BoomStick"
    x:Class="BoomStick.Views.Home"
    xmlns="http://xamarin.com/schemas/2014/forms"
    .......... />

    <d:AdFrame.Resources>
        <ResourceDictionary>
            <converter:FormatShortDateConveter x:Key="FormatShortDateConveter" />
            <converter:ConvertMonetaryFormat x:Key="ConvertMonetaryFormat" />
            <converter:BoolFromValue x:Key="BoolFromValue" />
            <converter:FormatNumberConverter x:Key="FormatNumberConverter" />
            <converter:ConvertLengthFromText x:Key="ConvertLengthFromText" />

            <DataTemplate x:Key="loadItemTemplate">
               ......
            </DataTemplate>
        </ResourceDictionary>
    </d:AdFrame.Resources>

    <d:AdFrame.ToolbarItems>
        <ToolbarItem
            Clicked="RefreshButtonClicked"
            IconImageSource="{Binding Path=RefreshIcon}"
            Text="Refresh" />
    </d:AdFrame.ToolbarItems>

    <d:AdFrame.Content>
        <ScrollView
            Margin="8"
            Orientation="Vertical"
            VerticalOptions="FillAndExpand">
            <AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
              ...............
            </AbsoluteLayout>
        </ScrollView>
    </d:AdFrame.Content>
</d:AdFrame>

Home.xaml.cs

    private HomeViewModel viewModel;

    public Home()
    {
        try
        {
            InitializeComponent();
            BindingContext = (viewModel = new HomeViewModel());

            InitilizePage();
        }
        catch (Exception ex)
        {
            DebugTools.LogException(ex);
        }
    }
       

In the code behind for this page however, if I call InitializeComponent(), then the parent page is never created, and only shows the page that called it. If I comment out the InitializeComponent() then the parent page is created, and I see the red and blue bars, but the content isn't shown.

As an alternate approach I tried creating a ControlTemplate that was the layout I wanted for the page, but the problem I had there is that the page being displayed in the ContentPresenter never get's it's ViewModel associated with it, so the page displays the XAML, but won't receive any data, or content that the ViewModel provides it.

    <ControlTemplate x:Key="AdEnabledPage">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.9*" />
                <RowDefinition Height="0.1*" />
            </Grid.RowDefinitions>
            <ContentPresenter BindingContext="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:Home}}}" Grid.Row="0" />
            <StackLayout Grid.Row="1" BackgroundColor="{StaticResource Key=cBackgroundColor}">
                <services:AdView />
            </StackLayout>
        </Grid>
    </ControlTemplate>

When I was trying this approach, I would put this code into the page that I wanted the ads to appear on.

        protected override async void OnAppearing()
        {
            base.OnAppearing();
            try
            {
                if (viewModel != null)
                    await ReloadPageData();

                if (!CoreGlobals.isPurchased)
                {
                    this.ControlTemplate = App.Current.Resources["AdEnabledPage"] as ControlTemplate;
                }
            }
            catch (Exception ex)
            {
                DebugTools.LogException(ex);
            }
        }

How can I correct this, so that a page is shown inside of the parent page.

1

There are 1 answers

0
Jesse Knott On

Okay, I found the problem. I had been basically trying to implement this using two different methods, taking half the solution from one and half from the other. Once I figured that out,I was able to get things working.

I was able to make a fix by using code from a MS sample repository. https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/templates-controltemplatedemos/

Cheers!