.NET MAUI BindableLayout Not Rendering on Page Load

I have been struggling with this for days and here I finally am to ask the community:
I have a .NET MAUI project that involves a page containing a BindableLayout in a StackLayout. The source data for the BindableLayout is a ContentView component that I am referencing from elsewhere in the project. The data in the viewmodel is loading in fine, everything is as the documentation says it should be. It’s just not loading the content when the page loads.
Here’s the super fun part:
If, while debugging with hot reload enabled, I backspace and replace the ‘.’ in the line

<reusableViews:BathroomView Content="{Binding .}"/>

the content then loads and everything carries on as I want it to.

Here is my xaml for the parent page:

<Grid>
    <Grid.RowDefinitions>
        <!--  Row 0: Fixed height for the button  -->
        <RowDefinition Height="*" />

        <!--  Row 1: Remaining space for the ScrollView  -->
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <!--  Row 1: ScrollView  -->
    <ScrollView
        x:Name="ScrollView"
        Grid.Row="0"
        VerticalOptions="FillAndExpand">
        <StackLayout>
            <!-- Display the BathroomView instances -->
            <StackLayout x:Name="BathroomsLayout" BindableLayout.ItemsSource="{Binding BathroomViews}">
                <BindableLayout.ItemTemplate>
                    <DataTemplate>
                        <!-- Directly render the BathroomView -->
                        <reusableViews:BathroomView Content="{Binding .}"/>
                    </DataTemplate>
                </BindableLayout.ItemTemplate>
            </StackLayout>
            <StackLayout x:Name="ScrollReference" />
        </StackLayout>
    </ScrollView>
    <AbsoluteLayout Grid.Row="0">
        <customControls:LoadingFrame x:Name="NewBathroomLoadingFrame" IsVisible="{Binding IsBusy}" />
    </AbsoluteLayout>
    <!--  Row 0: Button  -->
    <Border Grid.Row="1" Margin="0,10,0,10">
        <StackLayout Margin="0,10,0,10">
            <reusableViews:AdditionalCommentsView />
            <Button
                Clicked="AddNewBathroom_Clicked"
                HorizontalOptions="Center"
                Text="Add New Bathroom"
                VerticalOptions="Start" />
            <Button
                x:Name="BathroomsPageSubmitButton"
                Clicked="BathroomsPageSubmitButton_Clicked"
                Text="Submit Bathrooms Page" />
        </StackLayout>
    </Border>
</Grid>

cs for the parent page:

public partial class BathroomsPage
{
    private PageMaster _pageMaster;
    private PageFunctions _pageFunctions;
    private bool _navigated;

    public BathroomsPage(PageMaster pageMaster, PageFunctions pageFunctions)
    {
        _pageMaster = pageMaster;
        _pageFunctions = pageFunctions;
        InitializeComponent();

        BindingContext ??= _pageMaster.BathroomsPageViewModel;
    }
}

pageMaster and pageFunctions are singletons and are behaving fine.

Here is my BathroomView xaml:

<ContentView
    x:Class="..."
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:buttons="http://schemas.syncfusion.com/maui"
    xmlns:customControls="clr-namespace:..."
    xmlns:maui="clr-namespace:FFImageLoading.Maui;assembly=FFImageLoading.Maui"
    xmlns:reusableViews="clr-namespace:...">
    <ContentView.Resources>
        <ResourceDictionary>
            <Style TargetType="HorizontalStackLayout">
                <Setter Property="HorizontalOptions" Value="Center" />
            </Style>
            <Style TargetType="customControls:ErrorLabel">
                <Setter Property="Margin" Value="0,10,0,0" />
            </Style>
        </ResourceDictionary>
    </ContentView.Resources>
    <ContentView.Content>
        <Frame>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <buttons:SfExpander
                    x:Name="BathroomsExpander"
                    Margin="0,15,0,15"
                    AnimationDuration="150"
                    ClassId="BathroomsExpander"
                    HorizontalOptions="Center"
                    IsExpanded="False"
                    Expanded="BathroomsExpander_Expanded">
                    <buttons:SfExpander.Header>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="48" />
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="35" />
                                <ColumnDefinition Width="*" />
                            </Grid.ColumnDefinitions>
                            <HorizontalStackLayout HorizontalOptions="Center">
                                <Image
                                    Margin="40,0,2,2"
                                    Source="Resources/Images/bath.png"
                                    VerticalOptions="Center"
                                    WidthRequest="40" />
                                <Label
                                    Grid.Column="1"
                                    Margin="40,0,0,0"
                                    CharacterSpacing="0.25"
                                    FontAttributes="Bold"
                                    FontFamily="Roboto-Regular"
                                    FontSize="14"
                                    HorizontalTextAlignment="Center"
                                    Text="{Binding Bathroom.BathroomName}"
                                    VerticalOptions="CenterAndExpand" />
                            </HorizontalStackLayout>
                        </Grid>
                    </buttons:SfExpander.Header>
                    <buttons:SfExpander.Content>
                        <StackLayout Margin="0,10,0,0">
                            <StackLayout
                                Margin="0,0,20,0"
                                HorizontalOptions="Center"
                                Orientation="Horizontal">
                                <Label Text="Bathroom Name: " />
                                <Entry
                                    x:Name="BathOne"
                                    HorizontalTextAlignment="Center"
                                    Text="{Binding Bathroom.BathroomName}"
                                    WidthRequest="175" />
                                <!--<ImageButton
                                    x:Name="RemoveButton"
                                    Command="{Binding ViewModel.ClearNameCommand}"
                                    CommandParameter="{Binding Bathroom}"
                                    HeightRequest="30"
                                    Source="Resources/Images/remove.png"
                                    WidthRequest="15" />-->
                            </StackLayout>
                            <customControls:ErrorLabel Name="BathroomNameError" Text="Please give a name to this bathroom" />
                            <Label
                                FontAttributes="Bold"
                                HorizontalTextAlignment="Center"
                                Text="GFCI Outlets?" />
                            <StackLayout>
                                <HorizontalStackLayout>
                                    <RadioButton Content="Yes" IsChecked="{Binding Bathroom.GfciOutletsYes}" />
                                    <RadioButton Content="No" IsChecked="{Binding Bathroom.GfciOutletsNo}" />
                                    <RadioButton Content="No Outlet" IsChecked="{Binding Bathroom.GfciNoOutlet}" />
                                </HorizontalStackLayout>
                                <StackLayout x:Name="GfciFuncNonFuncLayout" IsVisible="{Binding Bathroom.GfciOutletsYes}">
                                    <HorizontalStackLayout HorizontalOptions="Center">
                                        <RadioButton Content="Functional" IsChecked="{Binding Bathroom.GfciFunc}" />
                                        <RadioButton Content="Non-Functional" IsChecked="{Binding Bathroom.GfciNonFunc}" />
                                    </HorizontalStackLayout>
                                    <StackLayout HorizontalOptions="Center" IsVisible="{Binding Bathroom.GfciNonFunc}">
                                        <Label HorizontalTextAlignment="Center" Text="Please describe the issue with the outlet:" />
                                        <Entry Text="{Binding Bathroom.GfciNonFuncEntry}" />
                                        <customControls:ErrorLabel Name="GfciNonFuncEntryError" Text="Please detail the problem with the outlet" />
                                    </StackLayout>
                                    <customControls:ErrorLabel Name="GfciFuncNonFuncError" Text="Please select functional or non-functional" />
                                </StackLayout>
                                <customControls:ErrorLabel Name="GfciYesNoNoOutletError" Text="Please select an option for the GFCI outlets" />
                            </StackLayout>
                            <StackLayout Margin="40,0,40,0">
                                <Border
                                    Margin="{OnPlatform Default='8,0,8,8',
                                                        WinUI='8,0,6,8',
                                                        MacCatalyst='8,0,6,8'}"
                                    Stroke="#CAC4D0"
                                    StrokeShape="RoundRectangle 8,8,8,8"
                                    StrokeThickness="{OnPlatform MacCatalyst=2,
                                                                 Default=1}"
                                    WidthRequest="{OnPlatform MacCatalyst=460,
                                                              WinUI=340}">
                                    <buttons:SfExpander AnimationDuration="150">
                                        <buttons:SfExpander.Header>
                                            <Grid>
                                                <Grid.RowDefinitions>
                                                    <RowDefinition Height="48" />
                                                </Grid.RowDefinitions>
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="35" />
                                                    <ColumnDefinition Width="*" />
                                                </Grid.ColumnDefinitions>
                                                <Image
                                                    Margin="14,2,2,2"
                                                    Source="Resources/Images/commentplus.png"
                                                    VerticalOptions="Center" />
                                                <Label
                                                    Grid.Column="1"
                                                    Margin="10,0,0,0"
                                                    CharacterSpacing="0.25"
                                                    FontFamily="Roboto-Regular"
                                                    FontSize="14"
                                                    Text="Additional Comments"
                                                    VerticalOptions="CenterAndExpand" />
                                            </Grid>
                                        </buttons:SfExpander.Header>
                                        <buttons:SfExpander.Content>
                                            <Editor
                                                Margin="20"
                                                HeightRequest="200"
                                                Placeholder="Enter any additional info here"
                                                Text="{Binding Bathroom.AdditionalCommentsText}" />
                                        </buttons:SfExpander.Content>
                                    </buttons:SfExpander>
                                </Border>
                                <Border>
                                    <StackLayout Margin="0,20,0,20">
                                        <HorizontalStackLayout>
                                            <Image
                                                Margin="10"
                                                Source="Resources/Images/imagesicon.png"
                                                WidthRequest="30" />
                                            <Label
                                                FontAttributes="Bold"
                                                FontSize="Large"
                                                HorizontalTextAlignment="Center"
                                                Text="Images" />
                                        </HorizontalStackLayout>
                                        <!--  UPDATE NAME OF BUTTON  -->
                                        <Button
                                            x:Name="AddBathroomPhotoButton"
                                            Clicked="AddBathroomPhotoButton_Clicked"
                                            Text="Add Photo"
                                            WidthRequest="150" />
                                        <StackLayout x:Name="ImagesLayout" />
                                    </StackLayout>
                                </Border>
                            </StackLayout>
                        </StackLayout>
                    </buttons:SfExpander.Content>
                </buttons:SfExpander>
                <StackLayout Grid.Row="1">
                    <Button
                        Command="{Binding ViewModel.RemoveCommand}"
                        CommandParameter="{Binding Bathroom}"
                        Text="Remove Bathroom" />
                </StackLayout>
            </Grid>
        </Frame>
    </ContentView.Content>
</ContentView>

And the BathroomView.xaml.cs:

using ECHI_Inspection_App_Maui.CustomControls;
using ECHI_Inspection_App_Maui.Helpers;
using Echi_Inspection_App_Maui.Models;
using Echi_Inspection_App_Maui.ViewModels.InspectionPageViewModels;
using Microsoft.Maui.Controls.Compatibility;
using Newtonsoft.Json;
using SQLite;
using Guid = System.Guid;
using FFImageLoading.Maui;
using StackLayout = Microsoft.Maui.Controls.StackLayout;
using System.Collections.ObjectModel;

namespace Echi_Inspection_App_Maui.Reusable_Views;

public partial class BathroomView
{
    public static readonly BindableProperty ViewModelProperty =
        BindableProperty.Create(nameof(ViewModel), typeof(BathroomsPageViewModel), typeof(BathroomView));

    public static readonly BindableProperty BathroomModelProperty =
        BindableProperty.Create(nameof(Bathroom), typeof(Bathroom), typeof(BathroomView));

    public BathroomsPageViewModel ViewModel
    {
        get => (BathroomsPageViewModel)GetValue(ViewModelProperty);
        set => SetValue(ViewModelProperty, value);
    }

    public Bathroom Bathroom
    {
        get => (Bathroom)GetValue(BathroomModelProperty);
        set => SetValue(BathroomModelProperty, value);
    }

    public BathroomView()
    {
        InitializeComponent();
    }

    private void DeletePhotoButton_Clicked(object? sender, EventArgs e)
    {
        if (sender is not Button button) return;
        // Get the parent StackLayout that contains both the CachedImage and the Button
        if (button.Parent is not StackLayout parentLayout) return;
        // Find the CachedImage within the parent StackLayout
        if (parentLayout.Children.FirstOrDefault(c => c is CachedImage) is not CachedImage cachedImage) return;
        // Remove the image source from your data collection
        var imageUrl = cachedImage.Source?.ToString();
        if (imageUrl != null)
        {
            Bathroom.Images.Remove(imageUrl);
            // Perform any additional cleanup or logic related to the image removal
            PageFunctions.DeletePhoto(imageUrl);
        }

        // Remove the CachedImage and the Button from the parent StackLayout
        parentLayout.Children.Remove(cachedImage);
        parentLayout.Children.Remove(button);
    }

    private async void AddBathroomPhotoButton_Clicked(object sender, EventArgs e)
    {
        var uploadedUrl = await PageFunctions.CapturePhotoAndGetUrl($"Interior WCF {Guid.NewGuid()}.jpg");

        if (uploadedUrl == null) return;

        Bathroom.Images.Add(uploadedUrl); //UPDATE THIS

        var deleteButton = new Button()
        {
            Text = "Delete",
            WidthRequest = 100,
            ClassId = "ImageButton",
        };

        deleteButton.Clicked += DeletePhotoButton_Clicked;

        ImagesLayout.Children.Add(new StackLayout()
        {
            Children =
            {
                new CachedImage()
                {
                    Source = uploadedUrl,
                    Aspect = Aspect.Center,
                    WidthRequest = 200
                },
                deleteButton
            }
        });
    }

    private static void AddImagesToStackLayout(StackLayout layout, ObservableCollection<string> images)
    {
        foreach (var image in images)
        {
            layout.Children.Add(new StackLayout()
            {
                Children =
                {
                    new CachedImage()
                    {
                        Source = image,
                        Aspect = Aspect.Center,
                        WidthRequest = 200
                    },
                    new Button()
                    {
                        Text = "Delete",
                        WidthRequest = 100,
                        ClassId = "ImageButton"
                    }
                }
            });
        }
    }

    private void BathroomsExpander_Expanded(object sender, Syncfusion.Maui.Expander.ExpandedAndCollapsedEventArgs e)
    {
        Bathroom.Images ??= [];

        if (!Bathroom.Images.Any()) return; //UPDATE THIS

        AddImagesToStackLayout(ImagesLayout, Bathroom.Images);

        var imageButtons = this.GetVisualTreeDescendants().OfType<Button>().Where(x => x.ClassId == "ImageButton")
            .ToList();

        foreach (var imageButton in imageButtons)
        {
            imageButton.Clicked += DeletePhotoButton_Clicked;
        }
    }
}

BathroomsExpander_Expanded() is there as an attempt to load the images at the bottom of the ContentView because of another problem that I won’t get into here.

Just to reiterate the fun part: I can delete and replace the ‘.’ in

<reusableViews:BathroomView Content="{Binding .}"/>

and everything renders fine. I am down to thinking that that is the biggest clue. It either has something to do with the order of rendering vs data, it’s a bug in the framework, or I’m just not using it correctly.

Should have just gone with Angular

2

First of all, BathroomView? Coolest name for a control ever!

<reusableViews:BathroomView Content=”{Binding .}”/>

Here you creating a new instance of BathroomView and setting its content to item of your ItemsSource, which is again an object of BathroomView.

This is not ideal. You can define a new ContentView instead and set it’s Content to you BathroomView object. Like below

 <DataTemplate>
        <!-- Directly render the BathroomView -->
        <ContentView Content="{Binding .}"/>
 </DataTemplate>

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật