Matt Kenefick
Matt Kenefick

Reputation: 1206

Can't access XAML elements after calling InitializeComponent on BasePage class

Edit: Here's a gist of some of the codebehinds and what it reports out: https://gist.github.com/mattkenefick/5c4effdbad712eb1a42f6cf7207226a6

Note: The question is specific to what's happening in an InitializeComponent call and not to much else. Please read the question in its entirety.

I have a MyPage->BasePage->ContentPage structure. If BasePage executes InitializeComponent() then the XAML elements lose scope within MyPage.. meaning I can't call MyPage.MyListView.ItemSource = xyz without getting a NullReferenceException.

If the InitializeComponent() call only happens in MyPage, then everything works fine (meaning that it doesn't get called in BasePage)

This question is very specifically about understanding why the BasePage.InitializeComponent() call breaks the references to XAML elements like x:MyListView.

   Models
   Pages
     ├ BasePage.xaml
     |   ⤷ BasePage.xaml.cs
     └ MyPage.xaml
         ⤷ MyPage.xaml.cs
   Views
   App.xaml
     ⤷ App.xaml.cs

On my MyPage.xaml markup, I have various StackLayout elements, ListView, etc. It all exists within a pages:BasePage.Content tag, like so:

<!-- for s/o: MyPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<pages:BasePage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:pages="clr-namespace:Namespace.Pages"
    xmlns:views="clr-namespace:Namespace.Views"
    x:Class="Namespace.Pages.MyPage">

    <pages:BasePage.Content>

        <ListView x:Name="ListViewView">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Image Source="{Binding imageUrl}" />
                            <Label Text="{Binding formattedDayOfWeek}" />
                            <Label Text="{Binding formattedDate}" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

    </pages:BasePage.Content>

</pages:BasePage>

Within my MyPage.xaml.cs class, the constructor executes the InitializeComponent() method.

Here's what the BasePage.xaml looks like:

<!-- for s/o: BasePage.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"
    x:Class="Namespace.Pages.BasePage"
    x:Name="_parent">

    <ContentPage.Content>
        <StackLayout x:Name="VerticalLayout" BackgroundColor="#f1f1f1">
            <ContentView
                x:Name="cv"
                x:FieldModifier="public"
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand"
                Content="{Binding Path=ViewContent, Source={x:Reference _parent}}" />
        </StackLayout>
    </ContentPage.Content>

</ContentPage>

So to reiterate:

Within the MyPage.xaml.cs, I'm trying to call ListViewView.ItemsSource = SomeDataModel after an async fetch from a REST server.

If the extended BasePage.xaml.cs class calls InitializeComponent() in its constructor... I will get a NullReferenceException when setting the ItemsSource.

If the extended BasePage.xaml.cs class DOES NOT call InitializeComponent() in its constructor... The ItemsSource is correctly set and the list appears.

Can someone explain to me why the parent InitializeComponent call causes the NullReferences in the MyPage class?

Thanks!

Upvotes: 0

Views: 316

Answers (2)

Ivan I
Ivan I

Reputation: 9990

First, I assume what you said is true (haven't tried it myself).

If that is the case this happens because your XAML is not inheritable the way C# class is inheritable. So what you do by inhering the XAML page is that you inherit ONLY its C# code, not its XAML contents. Having that in mind, InitializeComponent shouldn't work.

Upvotes: 0

Jason
Jason

Reputation: 89179

By default, the variables that Xamarin generates from your XAML are private, so you can't access them in inherited classes.

Field Modifers allow you change that default behavior

<Label x:Name="publicLabel" x:FieldModifier="public" />

Upvotes: 2

Related Questions