Reputation: 205
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:
x:DataType
on the ContentPage
.BindingContext
of an ancestor element using RelativeSource
like shown above.What I've tried:
MauiEnableXamlCBindingWithSourceCompilation
to false
, but this causes performance issues and I don't want to leave this disabled.<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.
Upvotes: 4
Views: 1003
Reputation: 21
I am facing the issue too, after upgrading to .netcore 9.0 and publishing to store. With error:
Invalid IL code in InitializeComponent (): IL_744e: stloc
I have to move <sys:Double x:Key="FlyoutItemIconSize">22</sys:Double>
to the global resources/application resources:
<Application.Resources>
<ResourceDictionary>
<sys:Double x:Key="FlyoutItemIconSize">22</sys:Double>
</ResourceDictionary>
</Application.Resources>
Then it is OK.
Upvotes: 0
Reputation: 205
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:
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}" />
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
).
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
.
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.
RoundButtonsMargin
and RoundButtonsRadius
. Removing these bindings resolved the error.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
.
Upvotes: 3
Reputation: 1503080
I haven't seen the exact problem you have (and the fact that you end up with invalid IL at the moment suggests this is worth reporting as a bug), but I'd suggest binding directly to your view model instead of going via the BindingContext
, in your Command
binding. I only learned about this yesterday, but if you use RelativeSource
with an AncestorType
which isn't an Element
-derived class, MAUI automatically uses a mode of FindAncestorBindingContext
. You can set the data type in the binding itself to indicate the type you're trying to bind to. It's slightly annoying to put the same type twice, but it works.
So for example, your binding would be (split over multiple lines here just for clarity, but obviously it all needs to be in the same line in your XAML):
Command="{Binding
Source={RelativeSource AncestorType={x:Type local:YourViewModelType}},
Path=EditPersonCommand,
x:DataType=local:YourViewModelType
}"
Upvotes: 7