# A quick introduction to Compiled Bindings in .NET applications and why you should use them

# Introduction

If you have already used modern .NET-based UI frameworks like [Xamarin.Forms](https://learn.microsoft.com/xamarin/get-started/what-is-xamarin-forms) and [.NET MAUI](https://learn.microsoft.com/dotnet/maui/what-is-maui) before, you are likely familiar with a common concept called [**data binding**](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/). This concept is used to access data from a data source *(such as properties inside a ViewModel)* from within the user interface and react to changes that may occur during runtime. Classic data binding in .NET based applications comes with a couple of drawbacks, though.

*Worst of all*, they are slow and inefficient, because classic bindings are resolved during *runtime* using [*reflection*](https://learn.microsoft.com/dotnet/csharp/programming-guide/concepts/reflection), which means that the binding source and target are scanned for matching properties and commands. *Secondly*, because of this runtime resolution, binding expressions are not evaluated during *compile-time*. This means that any incorrect bindings will only be noticed during runtime and potentially cause a crash or at the least a somewhat malfunctioning application. Finding these types of bugs can turn out to be a time consuming and finicky task. *Lastly*, classic bindings require some additional work in order to get useful code-completion suggestions and error messages in the XAML editor window.

Enter [compiled bindings](https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/data-binding/compiled-bindings). In order to speed up data binding and provide compile-time evaluation of binding expressions *(and also allow Intellisense and other tools to provide useful suggestions during design-time)*, compiled bindings were introduced. According to Microsoft, compiled bindings are up to **20 times (!) faster** than classic bindings\*\*. You should definitely use them\*\*, if you can\*\*.\*\*

> **Disclaimer:** This focus of this article is on compiled bindings in Xamarin.Forms and .NET MAUI. However, compiled bindings also exist in Windows Presentation Foundation (WPF), but they work quite differently and won't be covered in this blog article.

For this blog post, I will use a *.NET MAUI* project *(check out the* [**sample repository**](https://github.com/ewerspej/maui-samples)*)*, but these concepts largely apply also to Xamarin.Forms.

## Example ViewModel

For the bindings in this article, I will use the following, simplified ViewModel *(you can find the full code in the* [*sample repository*](https://github.com/ewerspej/maui-samples)*)*, which has a property called `Items` of type `ObservableCollection<BindingItem>` , an `AddItemCommand` to add new items and a `RemoveItemCommand` to remove either the last item or a specific item, depending on the `CommandParameter` that is passed in of type `BindingItem`:

```csharp
public partial class BindingsViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<BindingItem> _items = new();

    private int _counter = 0;

    [RelayCommand]
    private void AddItem()
    {
        Items.Add(new BindingItem
        {
            Name = $"Item {_counter}",
            Count = _counter
        });

        _counter++;
    }

    [RelayCommand]
    private void RemoveItem(BindingItem item = null)
    {
        if (Items.Count == 0)
        {
            return;
        }

        if (item == null)
        {
            Items.Remove(Items.Last());
            return;
        }

        Items.Remove(item);
    }
}
```

# Setting up Compiled Bindings

The central component for compiled bindings is the `x:DataType` attribute, which exists in both Xamarin.Forms and .NET MAUI. Basically, all we need to do is setting the `BindingContext` property and the `x:DataType` attribute in the View and we're good to go *(almost at least - there are some common pitfalls that I'll explain further down)*.

> **Note:** For compiled bindings to be usable, you need make sure that [XAML compilation](https://learn.microsoft.com/dotnet/maui/xaml/xamlc) is enabled in your project, which is the default for .NET MAUI. For [Xamarin.Forms](https://learn.microsoft.com/xamarin/xamarin-forms/xaml/xamlc) it needs to be explicitly enabled, either globally or locally. More recent versions of Xamarin.Forms also have XAML compilation enabled by default when using the official XAML templates.

## DataType and BindingContext

First, we need to set the `x:DataType` attribute of our `ContentPage` or `ContentView` on the root node in the XAML file to the underlying data type of the `BindingContext` of our View, which usually is a ViewModel:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
  x:Class="MauiSamples.Views.BindingsPage"
  xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:viewModels="clr-namespace:MauiSamples.ViewModels"
  x:DataType="viewModels:BindingsViewModel">
  <!-- ... -->
</ContentPage>
```

Here, I pass the name of the ViewModel including its namespace, so that it's *fully qualified*, because only fully-qualified names can be used in XAML compilation.

Once this is done, we still need to set the `BindingContext` for the View in the code-behind (the View's *.xaml.cs* file):

```csharp
public partial class BindingsPage : ContentPage
{
    public BindingsPage(BindingsViewModel viewModel)
    {
        InitializeComponent();
        BindingContext = viewModel;
    }
}
```

Done. This is all we need in order to set up compiled bindings. Pretty simple 😊.

Technically, this could also be done entirely in XAML, but I'll stick to the more common way of setting the `BindingContext` in the code-behind, which is also what I recommend to you, because in real world applications, you would typically use dependency injection to pass a ViewModel instance into the View instead of instantiating it yourself in the View's XAML or code-behind.

> **Note:** The `x:DataType` can be defined on any level in the XAML hierarchy and then only applies downward from that level in the XAML tree. However, it is recommended to set the `x:DataType` on the root level and then redefine it on deeper nested elements, if necessary (more on this further down).

## Using Bindings

Now, with the `x:DataType` and the `BindingContext` set, we can go ahead and bind to the properties and commands of our ViewModel as usual:

```xml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
  x:Class="MauiSamples.Views.BindingsPage"
  xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:models="clr-namespace:MauiSamples.Models"
  xmlns:viewModels="clr-namespace:MauiSamples.ViewModels"
  x:DataType="viewModels:BindingsViewModel">

  <Grid
    RowDefinitions="*, auto"
    RowSpacing="8">

    <ListView
      Grid.Row="0"
      ItemsSource="{Binding Items}">
      <!-- ... -->
    </ListView>

    <HorizontalStackLayout
      Grid.Row="1">
      <Button
        Command="{Binding AddItemCommand}"
        Text="Add Item" />
      <Button
        Command="{Binding RemoveItemCommand}"
        Text="Remove Last" />
    </HorizontalStackLayout>

  </Grid>

</ContentPage>
```

## Design-time Hints

Apart from the massive boost in binding performance, one of the main differences between compiled bindings and classic bindings is that Visual Studio *(as well as plugins and extensions or any other modern IDE)* now recognizes the properties and commands and can provide suggestions and errors for the bindings to us during *design-time*, which means that we can already see in the text editor when something isn't set correctly even *before* compiling and running the app:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674637244753/a41364f1-5b2f-48b6-bd58-3f97983d53c1.png align="center")

Here, we can see that `AddItem` is inaccessible, because it's a private method and doesn't exist as a command. The correct name of the command to bind to is `AddItemCommand` *(since we are using* [*MVVM Source Generators*](https://ewerspej.hashnode.dev/introduction-to-mvvm-source-generators-for-dotnet) *here)*. If we would try to run the app like this, we would receive compiler errors, which wouldn't happen with classic bindings.

> **Note:** I am using the [JetBrains ReSharper](https://www.jetbrains.com/resharper/) extension for Visual Studio (no affiliation), which provides the **Inlay Hints** for the data context in XAML (notice the `(BindingsViewModel).Path=`). This is additionally useful, because I can directly see what my current `BindingContext` is.

# Nested Data Contexts

There are various common scenarios, where the data context in the View hierarchy changes. On the root level of our View, we have a ViewModel set as the data context (*meaning it's set as the* `BindingContext`), but we may have other types that we are using in our ViewModel that are represented in our View hierarchy based on a `DataTemplate`.

The most common case is when we have a binding to a *List* or *Collection* of items of a specific type in our ViewModel, such as the `ObservableCollection<BindingItem>` in the `BindingsViewModel`. Using classic bindings, our View only knows at runtime, through reflection, what type the items in the *List* or *Collection* have.

With compiled bindings, we can already tell the View at *compile-time*, *before* the actual `BindingContext` is set (which only occurs during *runtime*), what type the `BindingContext` will have. Now, this `BindingContext` usually applies to the entire `ContentPage` or `ContentView` that we're dealing with.

This is problematic, because the data context of a `DataTemplate` usually is inferred from the type of the items that are contained inside of a *List* or *Collection*.

In the following situation, the `DataTemplate` will assume that the data context has not changed and will fail to bind to the `Name` property of the `BindingItem` element, because the `x:DataType` currently applies to the entire View:

```xml
 <ContentPage
  x:Class="MauiSamples.Views.BindingsPage"
  xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:models="clr-namespace:MauiSamples.Models"
  xmlns:viewModels="clr-namespace:MauiSamples.ViewModels"
  x:DataType="viewModels:BindingsViewModel">

  <Grid
    RowDefinitions="*, auto"
    RowSpacing="8">

    <ListView
      Grid.Row="0"
      ItemsSource="{Binding Items}">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <Grid
              Padding="8"
              ColumnDefinitions="*,*">
              <Label
                Grid.Column="0"
                HorizontalOptions="Start"
                Text="{Binding Name}"
                VerticalTextAlignment="Center" />
            </Grid>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  <!-- ... -->
  </Grid>
</ContentPage>
```

When we try to build and run this, we'll receive the following compiler error:

> Error XFC0045 Binding: Property "Name" not found on "MauiSamples.ViewModels.BindingsViewModel".

This is because the `Name` property doesn't exist in the `BindingsViewModel`, but belongs to the `BindingItem` model class instead.

In order to resolve this, we can *redefine* the `x:DataType` anywhere in the View hierarchy. For this, all we need to do is specify the `BindingItem` class as the `x:DataType` on the `DataTemplate` and then the bindings will work as expected:

```xml
<ContentPage
  x:Class="MauiSamples.Views.BindingsPage"
  xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  xmlns:models="clr-namespace:MauiSamples.Models"
  xmlns:viewModels="clr-namespace:MauiSamples.ViewModels"
  x:DataType="viewModels:BindingsViewModel">

  <Grid
    RowDefinitions="*, auto"
    RowSpacing="8">

    <ListView
      Grid.Row="0"
      ItemsSource="{Binding Items}">
      <ListView.ItemTemplate>
        <!-- redefining the DataType here -->
        <DataTemplate x:DataType="models:BindingItem">
          <ViewCell>
            <Grid
              Padding="8"
              ColumnDefinitions="*,*">
              <Label
                Grid.Column="0"
                HorizontalOptions="Start"
                Text="{Binding Name}"
                VerticalTextAlignment="Center" />
            </Grid>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  <!-- ... -->
  </Grid>
</ContentPage>
```

When we run the app now, we'll see that our bindings work correctly and we can add and remove items using the provided buttons:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674639339901/a0a68da4-ad07-408d-94dd-d69ca1ff40ab.png align="center")

Neat, we can now use compiled bindings and redefine the `x:DataType` on any level of the View hierarchy based on the structure of our ViewModel 😃. Which leads us to parent bindings, which are also affected by the `x:DataType`.

# Relative Bindings

Instead of just removing the last item, it would be great if we could also remove any individual item in the `ObservableCollection`. For this, we need to add a button next to the label in the `DataTemplate`, bind to the `RemoveItemCommand` and pass the item itself as the `CommandParameter`:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674639646686/8a3d6934-d77d-4562-9b91-a9033e13d9f2.png align="center")

***But wait***, the `RemoveItemCommand` cannot be found 😱 and the *design-time* inlay hint already shows us why: The data context is currently set to `BindingItem`, because we specified that as the data type for our `DataTemplate` earlier:

```xml
<DataTemplate x:DataType="models:BindingItem">
```

> **Note:** This issue applies to classic bindings and compiled bindings alike. So does the solution for the issue. The difference is that we can now already identify the problem during design-time or compile-time instead of noticing the problem during runtime only.

This is where [relative bindings](https://learn.microsoft.com/dotnet/maui/fundamentals/data-binding/relative-bindings) come in. When trying to bind to a property or command of a parent data context, such as our `BindingsViewModel`, we need to ensure that the binding is set to the correct *Binding Source*, specified via the `Source` attribute of a binding expression. In our case, we need to specify a `RelativeSource` with an `AncestorType` which is set our `BindingsViewModel`:

```xml
<Button
  Grid.Column="1"
  Text="Remove"
  BackgroundColor="Red"
  Command="{Binding RemoveItemCommand, Source={RelativeSource AncestorType={x:Type viewModels:BindingsViewModel}}}"
  CommandParameter="{Binding .}" />
```

By doing this, we're telling the binding mechanism to look higher up in the View hierarchy for a data context that matches the type of the specified `RelativeSource`.

Now, the error is gone and we can build and run the app successfully and add and remove items as much as we like, even individual ones:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1674640691212/64eab7f2-05eb-4b5d-b7c3-486e6662cb9e.png align="center")

Awesome, our bindings work and we can now benefit from the performance boost as well as the *compile-time* (and also *design-time*) evaluation of binding expressions that compiled bindings provide 🏆.

# Conclusions and next steps

Compiled bindings are awesome and highly convenient. Especially the performance boost and the compile-time (as well as design-time) evaluation of binding expressions are gold.

You cannot only speed up your application using compiled bindings, but you can also reduce your debugging efforts and focus on developing quality features instead of hunting for bugs that turn out to be binding errors, which often are difficult to identify without IDE support.

Have you used compiled bindings before? Did you run into any issues with them? Let me know and I'll be happy to write another blog post to explore bindings even further.

If you enjoyed this blog post, then follow me on [**LinkedIn**](https://www.linkedin.com/in/jewerspeters), subscribe to this [**blog**](https://hashnode.com/@ewerspej) and star the [**GitHub**](https://github.com/ewerspej/maui-samples) repository for this post so you don't miss out on any future posts.
