.NET MAUI iOS Bug – Release Mode Bindings Not Working

Software bugs can sometimes manifest in specific environments or platforms, leading to unexpected behavior. In this blog post, we will discuss a bug in .NET MAUI specifically affecting iOS platforms. The bug causes bindings between Views and ViewModels to fail in Release mode, resulting in empty Views without the expected data. We’ll explore the symptoms of this bug and present a workaround that involves utilizing the XamlCompilation attribute with the Skip option.

Symptoms

The bug we are addressing affects the binding functionality in .NET MAUI apps running on iOS platforms in Release mode. When encountering this issue, Views fail to bind with their associated ViewModels, resulting in empty Views that appear as if no BindingContext is present.

What is XamlCompilation?

XamlCompilation is an attribute provided by Xamarin.Forms that allows developers to specify how XAML files should be compiled. It offers three options: None, XamlC, and Skip.

  • None: The default option. XAML files are not compiled individually and are interpreted at runtime.
  • XamlC: XAML files are compiled ahead-of-time into IL code, improving performance by eliminating the need for runtime interpretation.
  • Skip: XAML files are skipped from the compilation process and are interpreted at runtime, similar to the None option.

Providing workaround

To mitigate this bug, we can utilize the XamlCompilation attribute with the Skip option on the affected Views. This attribute is used to control the compilation behavior of XAML files in .NET MAUI applications.

Identify the Affected View First, identify the View(s) in your .NET MAUI app that are experiencing the binding issue specifically on iOS platforms in Release mode. Add XamlCompilation Attribute Add the XamlCompilation attribute to the affected View’s code-behind file. Set the attribute value to Skip to instruct the compiler to exclude the associated XAML file from the compilation process.

[XamlCompilation(XamlCompilationOptions.Skip)]
public partial class MyAffectedView : ContentView
{
    // ...
}

Ensure that the affected View is properly updated with the XamlCompilation attribute. Test the application in Release mode on iOS to confirm that the bindings are now functioning as expected and the Views are populated with the appropriate data.

Keep in mind that while this workaround provides a solution for the bug, it’s important to regularly check for updates from the .NET MAUI team, as they may address and fix this issue in future releases. Additionally, test your app thoroughly after applying the workaround to ensure the expected functionality and behavior across different platforms.

This content has 11 months. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

The easiest and best MVVM toolkit also for .NET MAUI.

Guys, this is unbelievable. Microsoft Community toolkit has added a tool that means you never have to worry about which MVVM framework you’re voting for. None can be as good as this one. It can be used not only for .NET MAUI, but also for other technologies (e.g. WPF). But before I sing your praises, let’s look at what this package has to offer us:

Introducing CommunityToolkit.Mvvm

First of all, you don’t need to implement INotifyPropertyChanged in ViewModels. And by that I mean not even its parent classes. Since this component is a code generator, it’s enough to make your classes partial, and the INotifyPropertyChanged implementation is done by it, instead of you. This means that your class will be supplemented with the best implementation so that you can easily use common methods like SetProperty in the setter branch of your properties. Okay, okay, but almost any MVVM framework could do this, and you didn’t even need to use a code generator, just simply derive from a parent class. I’ll do you one better: what if you didn’t have to write the setter and getter branches of the properties at all? What if the toolkit made them itself? If you could implement a property declaration from two lines of code?
Well, that’s what makes this toolkit good: It does all that for you.

And if that’s not enough, you don’t even have to bother creating Commands. The toolkit automatically creates a command for you from your methods.
But let’s see how you can use it.

Use CommunityToolkit.MVVM in .NET MAUI

Let’s see what my ViewModel looked like before I used this toolkit:

namespace Carloo.ViewModels.SignInPage
{
    public partial class SignInPageViewModel : BaseViewModel
    {
        private Command signInCommand;
        public Command SignInCommand
        {
            get { return signInCommand ?? (signInCommand = new Command(() => _ = SignIn(), () => true)); }
        }

        private string userName;
        public string UserName
        {
            get { return userName; }
            set { SetProperty(ref userName, value, nameof(UserName)); }
        }

        private string password;
        public string Password
        {
            get { return password; }
            set { SetProperty(ref password, value, nameof(Password)); }
        }

        public SignInPageViewModel()
        {

        }

        private void SignIn()
        {
            try
            {
                ...
            }
            catch (Exception ex)
            {
                // todo error
            }
        }
    }
}

And now let’s see what this Toolkit can do with source code to make life easier:

namespace Carloo.ViewModels.SignInPage
{
    [INotifyPropertyChanged]
    public partial class SignInPageViewModel
    {
        [ObservableProperty]
        private string userName;

        [ObservableProperty]
        private string password;

        public SignInPageViewModel()
        {

        }

        [ICommand]
        private void SignIn()
        {
            try
            {
                ...
            }
            catch (Exception ex)
            {
                // todo error
            }
        }
    }
}

In other ways, however, Resharper also had the tools to provide similar simplicity for implementing MVVM. But if I remember correctly, it didn’t have all that. Like, say, that you could release PropertyChanged on a completely unrelated property, even with an attribute declaration, without writing code.

        [ObservableProperty]
        [AlsoNotifyFor(nameof(CanSignIn))]
        private string _userName;

        [ObservableProperty]
        [AlsoNotifyFor(nameof(CanSignIn))]
        private string _password;


        public bool CanSignIn { get => !string.IsNullOrWhiteSpace(UserName) && !string.IsNullOrWhiteSpace(Password); }

Learn more

I am very excited about using this tool in production environments. If you want to learn more about it, please visit the following pages:
https://devblogs.microsoft.com/ifdef-windows/announcing-net-community-toolkit-v8-0-0-preview-1/
https://docs.microsoft.com/en-gb/windows/communitytoolkit/mvvm/introduction

This content has 2 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.

Xamarin.Forms: Lazy loaded Command snippet for Visual Studio

Use xamcomm tab tab to generate commands in ViewModels with this snippet

IntelliSense recommendation

Snippet in work

Save the snippet file as {name}.snippet in Visual Studio’s folder: C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC#\Snippets\1033\Visual C#

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
	<CodeSnippet Format="1.0.0">
		<Header>
			<Title>Xamarin Lazy Command</Title>
			<Shortcut>xamcomm</Shortcut>
			<Description>Xamarin Command declaration code snippet for MVVM design pattern</Description>
			<Author>banditoth.hu</Author>
			<SnippetTypes>
				<SnippetType>Expansion</SnippetType>
			</SnippetTypes>
		</Header>
		<Snippet>
			<Declarations>
				<Literal>
					<ID>BackFieldName</ID>
					<ToolTip>Backfield Name</ToolTip>
					<Default>_backfieldname</Default>
				</Literal>
				<Literal>
					<ID>CommandName</ID>
					<ToolTip>Command name</ToolTip>
					<Default>CommandName</Default>
				</Literal>
				<Literal>
					<ID>ActionToExecute</ID>
					<ToolTip>Action to execute</ToolTip>
					<Default>() => { return; /*TODO: Implement logic for this Command*/ }</Default>
				</Literal>
        <Literal>
          <ID>ActionCanExecute</ID>
          <ToolTip>Action to determine can execute</ToolTip>
          <Default>() => true</Default>
        </Literal>
			</Declarations>
			<Code Language="csharp">
			<![CDATA[
	private Command $BackFieldName$Command;

	public Command $CommandName$Command
	{
		get { return $BackFieldName$Command ?? ($BackFieldName$Command = new Command($ActionToExecute$,$ActionCanExecute$)); }
	}
	$end$]]>
			</Code>
		</Snippet>
	</CodeSnippet>
</CodeSnippets>
This content has 3 years. Some of the information in this post may be out of date or no longer work. Please, read this page keeping its age in your mind.