xamlbindingmaui

Issue with Binding and RelativeSource in .NET MAUI 9 - XamlCompilationOptions.Skip Needed for Release Build


I'm working on a .NET MAUI 9 application and running into a problem with binding when using RelativeSource while setting x:DataType in XAML. Specifically, when I try to bind a command in my ContentPage to a button using RelativeSource, I get an error during the release build. The error mentions "Invalid IL code" during InitializeComponent unless I set XamlCompilationOptions.Skip.

Here's the code I am working with:

    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="entities:Person">
            <!--  Person Frame  -->
            <Border
                Margin="{StaticResource PersonFrameMargin}"
                Padding="{StaticResource PersonFramePadding}"
                Background="{StaticResource LightGradientBackground}">
                <Border.StrokeShape>
                    <RoundRectangle CornerRadius="{StaticResource PersonFrameRadius}" />
                </Border.StrokeShape>
                <!--  Person Grid  -->
                <Grid Margin="{StaticResource PersonGridMargin}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="auto" />
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="auto" />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="auto" />
                        <RowDefinition Height="auto" />
                    </Grid.RowDefinitions>
                    <!--  Circular Person Image  -->
                    <Border
                        Grid.Column="0"
                        Margin="{StaticResource PersonImageMargin}"
                        Background="{StaticResource LightGradientBackground}"
                        HeightRequest="{StaticResource PersonImageSize}"
                        HorizontalOptions="Center"
                        VerticalOptions="Start"
                        WidthRequest="{StaticResource PersonImageSize}">
                        <Border.StrokeShape>
                            <RoundRectangle CornerRadius="{StaticResource PersonImageCornerRadius}" />
                        </Border.StrokeShape>
                        <Image
                            Aspect="AspectFill"
                            HeightRequest="{StaticResource PersonImageSize}"
                            WidthRequest="{StaticResource PersonImageSize}">
                            <Image.Source>
                                <MultiBinding Converter="{StaticResource ByteArrayAndGenderToImageSourceConverter}">
                                    <Binding Path="Image.Value" />
                                    <Binding Path="Gender" />
                                </MultiBinding>
                            </Image.Source>
                        </Image>
                    </Border>
                    <!--  Person Name And Date StackLayout  -->
                    <StackLayout Grid.Column="1" Spacing="{StaticResource PersonNameAndDateStackLayoutSpacing}">
                        <Label
                            FontAttributes="Bold"
                            FontSize="Large"
                            LineBreakMode="WordWrap"
                            Text="{Binding FullName}" />
                        <Label FontSize="Medium" Text="{Binding LastInquiringDate.Value, StringFormat={StaticResource PersonDateFormat}}" />
                    </StackLayout>
                    <!--  Person Round Edit Button  -->
                    <Button
                        Grid.Column="2"
                        Margin="{StaticResource PersonRoundEditButtonMargin}"
                        Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.EditPersonCommand}"
                        CommandParameter="{Binding Id}"
                        CornerRadius="{StaticResource PersonRoundEditButtonRadius}"
                        HeightRequest="{StaticResource PersonRoundEditButtonSize}"
                        ImageSource="pencil.png"
                        VerticalOptions="Start"
                        WidthRequest="{StaticResource PersonRoundEditButtonSize}" />

                    <!--  Phone Numbers StackLayout  -->
                    <StackLayout
                        Grid.Row="1"
                        Grid.Column="0"
                        Grid.ColumnSpan="3">
                        <BoxView
                            Margin="{StaticResource LineMargin}"
                            Background="{StaticResource White}"
                            HeightRequest="{StaticResource LineSize}" />
                        <!--  Phone Numbers Label  -->
                        <Label FontSize="Large" Text="Phone Numbers:" />
                        <!--  Phone Numbers StackLayout  -->
                        <CollectionView ItemsSource="{Binding PhoneNumbers}" SelectionMode="None">
                            <CollectionView.ItemTemplate>
                                <DataTemplate x:DataType="entities:PhoneNumber">
                                    <!--  Phone Grid  -->
                                    <Grid Margin="{StaticResource PhoneGridMargin}">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*" />
                                            <ColumnDefinition Width="auto" />
                                        </Grid.ColumnDefinitions>
                                        <!--  Phone Numbers  -->
                                        <Label
                                            Grid.Column="0"
                                            FontAttributes="Bold"
                                            FontSize="Medium"
                                            Text="{Binding FullNumber}"
                                            VerticalOptions="Center" />
                                        <!--  WhatsApp Button  -->
                                        <ImageButton
                                            Grid.Column="1"
                                            Command="{Binding Path=BindingContext.SendWhatsAppMessagesCommand, Source={RelativeSource AncestorType={x:Type ContentPage}}}"
                                            CommandParameter="{Binding}"
                                            HeightRequest="{StaticResource WhatsAppButtonSize}"
                                            HorizontalOptions="End"
                                            Source="whatsapp_icon.png"
                                            WidthRequest="{StaticResource WhatsAppButtonSize}" />
                                    </Grid>
                                </DataTemplate>
                            </CollectionView.ItemTemplate>
                        </CollectionView>
                    </StackLayout>
                </Grid>
            </Border>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

More specifically the Edit button:

<!--  Person Round Edit Button  -->
                    <Button
                        Grid.Column="2"
                        Margin="{StaticResource PersonRoundEditButtonMargin}"
                        Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.EditPersonCommand}"
                        CommandParameter="{Binding Id}"
                        CornerRadius="{StaticResource PersonRoundEditButtonRadius}"
                        HeightRequest="{StaticResource PersonRoundEditButtonSize}"
                        ImageSource="pencil.png"
                        VerticalOptions="Start"
                        WidthRequest="{StaticResource PersonRoundEditButtonSize}" />

The problem arises because I need to use RelativeSource to bind a command from the ContentPage's BindingContext, but I also have x:DataType set for type safety and the need to pass the Person's Id along with the command. When I don't disable XAML compilation by using:

<MauiEnableXamlCBindingWithSourceCompilation>false</MauiEnableXamlCBindingWithSourceCompilation>

The solution won't build and throws XC0045 error.

Steps to reproduce:

  1. Set x:DataType on the ContentPage.
  2. Bind a command from the BindingContext of an ancestor element using RelativeSource like shown above.
  3. Attempt to build the project in release mode.

What I've tried:

<TrimMode>partial</TrimMode>
<Optimize>false</Optimize>
<AndroidLinkMode>None</AndroidLinkMode>

However, these changes did not resolve the issue.

Expected behavior: The binding should work without needing to disable XAML compilation and should allow the command to be bound correctly from the ViewModel.

Question: Is there a way to correctly use RelativeSource binding with x:DataType in .NET MAUI 9 without encountering the "Invalid IL code" error in release builds? If not, is there a workaround to avoid disabling XAML compilation?

Update:

Using inline x:DataType as Jon Skeet suggested does remove the XC0045 error when building.

I will update the solution and test the release build again, and then I will update this question accordingly.


Solution

  • The issue I encountered in my .NET MAUI 9 application was caused by two separate problems, both related to binding and XAML compilation. I will break down the solutions for each problem below:

    Problem 1: Invalid IL Code Error

    The first issue was the "Invalid IL Code" error that occurred during the release build. This problem was caused by using StaticResource bindings for properties like:

    <Thickness x:Key="RoundButtonsMargin">5,10</Thickness>
    <x:Double x:Key="RoundButtonsSize">50</x:Double>
    <x:Int32 x:Key="RoundButtonsRadius">25</x:Int32>
    

    and binding them like this:

    <Button
        Grid.Column="1"
        Margin="{StaticResource RoundButtonsMargin}"
        Command="{Binding CancelCommand}"
        CornerRadius="{StaticResource RoundButtonsRadius}"
        HeightRequest="{StaticResource RoundButtonsSize}"
        ImageSource="cancel.png"
        WidthRequest="{StaticResource RoundButtonsSize}" />
    

    Solution to Invalid IL Code Error:

    The fix for this issue was removing the usage of the static resources (RoundButtonsMargin and RoundButtonsRadius). Once I removed these static resource bindings, the "Invalid IL Code" error during the release build was resolved. After making this change, the app started successfully without throwing the error at runtime.

    I still might need to do more tests to see if the issue relates to using mismatched data types (e.g., using Single instead of Double).


    Problem 2: XC0045 Error when Building Solution

    The second issue I encountered was the XC0045 error when building the solution, which was related to the use of x:DataType in conjunction with RelativeSource.

    Solution to XC0045 Error:

    To resolve this error, I followed Jon Skeet's suggestion to use inline x:DataType within the Path instead of separately binding it with AncestorType only, which fixed the error. Jon mentioned that when using RelativeSource with an AncestorType that isn’t element-derived, MAUI automatically uses the FindAncestorBindingContext mode. He recommended binding directly to the view model instead of accessing the BindingContext through RelativeSource, and using inline x:DataType for clarity and to resolve the issue.

    Here's how I modified the code:

    Instead of:

    <Button
        Grid.Column="2"
        Margin="{StaticResource PersonRoundEditButtonMargin}"
        Command="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.EditPersonCommand}"
        CommandParameter="{Binding Id}"
        CornerRadius="{StaticResource PersonRoundEditButtonRadius}"
        HeightRequest="{StaticResource PersonRoundEditButtonSize}"
        ImageSource="pencil.png"
        VerticalOptions="Start"
        WidthRequest="{StaticResource PersonRoundEditButtonSize}" />
    

    I used:

    <Button
        Grid.Column="2"
        Margin="{StaticResource PersonRoundEditButtonMargin}"
        Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodels:OverdueInquiringViewModel}}, Path=EditPersonCommand, x:DataType=viewmodels:OverdueInquiringViewModel}"
        CommandParameter="{Binding Id}"
        CornerRadius="{StaticResource PersonRoundEditButtonRadius}"
        HeightRequest="{StaticResource PersonRoundEditButtonSize}"
        ImageSource="pencil.png"
        VerticalOptions="Start"
        WidthRequest="{StaticResource PersonRoundEditButtonSize}" />
    

    Using inline x:DataType for the Command binding resolved the XC0045 error and allowed the solution to build successfully.


    Summary:

    1. Invalid IL Code Error: The issue was caused by using static resources for properties like RoundButtonsMargin and RoundButtonsRadius. Removing these bindings resolved the error.
    2. XC0045 Error: The solution was to use inline x:DataType for the Command binding, as suggested by Jon Skeet.

    With these two changes, my app now builds correctly in release mode without requiring XamlCompilationOptions.Skip.