github CommunityToolkit/Maui 14.0.0
14.0.0: Introducing Bindable Property Source Generators!

latest releases: 6.0.0-camera, 8.0.0-mediaelement
5 hours ago

CommunityToolkit.Maui v14.0.0

I am excited to debut two new source generators!

  • [BindableProperty]
  • [AttachedBindableProperty<T>]

These new source generators make it easier than ever to create a BindableProperty for your .NET MAUI apps by allowing us to write all the associated boiler-plate code for you. In fact, all Bindable Properties in CommunityToolkit.Maui are now automatically generated using these new source generators!

For an in-depth overview of both [BindableProperty] and [AttachedBindableProperty<T>], check out my comprehensive write-up:
👉 https://codetraveler.io/2026/01/29/introducing-bindable-property-source-generators/

Bug Fixes + New Features

Alongside the new Source Generators, this Release also brings important bug fixes and new features:

  • StatusBarBehavior
    • Fixes StatusBarBehavior on Android 35+
  • Snackbar
    • Fixes a memory leak
  • Popup
    • Add extension method for NavigatedFromEventArgs
    • Fixes tapping issues when using a CollectionView in Popup
    • Fixes a memory leak

Introducing Bindable Property Source Generators

Opt-into this Experimental Feature

We have decided to release this feature using the [Experimental] attribute. This allows us to let you try out its features and provide feedback while giving us the flexibility to update it with your feedback over the next few releases.

In the csproj file of your .NET MAUI app, add the following code:

<!-- Opt into Binbdable Property Source Generators -->
<PropertyGroup>
    <NoWarn>MCTEXP001</NoWarn>
</PropertyGroup>

In the mean-time, we will be writing more comprehensive documentation and writing Analyzers, similar to CommunityToolkit.MVVM, to help provide you the best coding experience!

Using [BindableProperty]

To leverage the Bindable Property Source Generator, first ensure you using a partial class. Then, add the partial keyword and attach the [BindableProperty] attribute to your property:

  1. Add the partial keyword to the class
  2. Add the partial keyword the Property for which to generate an associated Bindable Property
  3. Add the [BindableProperty] attribute to the property to make it bindable
using CommunityToolkit.Maui;

namespace BindablePropertySourceGeneratorSample;

public partial class CustomButton : Button
{
    [CommunityToolkit.Maui.BindableProperty] 
    public partial int? Number { get; set; }
}

The above example generates the following Bindable Property:

public partial class CustomButton
{
    /// <summary>
    /// BindableProperty for the <see cref = "Number"/> property.
    /// </summary>
    public static readonly global::Microsoft.Maui.Controls.BindableProperty NumberProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Number", typeof(int?), typeof(BindablePropertySourceGeneratorSample.CustomButton), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
    public partial int? Number { get => (int)GetValue(NumberProperty); set => SetValue(NumberProperty, value); }
}

For more in-depth examples see my comprehensive write-up on [BindableProperty]:
👉 https://codetraveler.io/2026/01/29/introducing-bindable-property-source-generators/

Using [AttachedBindableProperty<T>]

Using [AttachedBindableProperty] is a bit different. This attribute is applied to either the Class or the Constructor, not a Property, as Attached Properties are not required to have an associated Property:

.NET Multi-platform App UI (.NET MAUI) attached properties enable an object to assign a value for a property that its own class doesn't define

To leverage the Attached Bindable Property Source Generator, first ensure you using a partial class. Then, add [AttachedBindableProperty<T>] attribute to either the class or constructor:

  1. Add the partial keyword to the class
  2. Add the [AttachedBindableProperty<T>] attribute to the class or constructor for which to generate an associated Attached Property
namespace BindablePropertySourceGeneratorSample;

[CommunityToolkit.Maui.AttachedBindableProperty<int>("Number")] 
public partial class CustomButton : Button
{
  public CustomButton()
  {
  }
}

Alternatively, the attribute can be attached to the constructor:

namespace BindablePropertySourceGeneratorSample;
 
public partial class CustomButton : Button
{
  [CommunityToolkit.Maui.AttachedBindableProperty<int>("Number")]
  public CustomButton()
  {
  }
}

Both examples above generate the following Attached Property along with its associated Get/Set methods:

public partial class CustomButton
{
    /// <summary>
    /// Attached BindableProperty for the Number property.
    /// </summary>
    public static readonly global::Microsoft.Maui.Controls.BindableProperty NumberProperty = global::Microsoft.Maui.Controls.BindableProperty.CreateAttached("Number", typeof(int), typeof(BindablePropertySourceGeneratorSample.CustomButton), null, (global::Microsoft.Maui.Controls.BindingMode)0, null, null, null, null, null);
    /// <summary>
    /// Gets Number for the <paramref name = "bindable"/> child element.
    /// </summary>
    public static int GetNumber(global::Microsoft.Maui.Controls.BindableObject bindable) => (int)bindable.GetValue(NumberProperty);
    /// <summary>
    /// Sets Number for the <paramref name = "bindable"/> child element.
    /// </summary>
    public static void SetNumber(global::Microsoft.Maui.Controls.BindableObject bindable, int value) => bindable.SetValue(NumberProperty, value);
}

For more in-depth examples see my comprehensive write-up on [AttachedBindableProperty<T>]:
👉 https://codetraveler.io/2026/01/29/introducing-bindable-property-source-generators/

Breaking Changes

FileSaver

We heard your feedback! You now have more control over which Permissions you require and when you request them.

Developers must now manually request Permissions.StorageRead and Permissions.StorageWrite:

var readPermissionsRequest = await Permissions.RequestAsync<Permissions.StorageRead>();
var writePermissionsRequest = await Permissions.RequestAsync<Permissions.StorageWrite>();

FolderPicker

We heard your feedback! You now have more control over which Permissions you require and when you request them.

Developers must now manually request Permissions.StorageRead and Permissions.StorageWrite:

var readPermissionsRequest = await Permissions.RequestAsync<Permissions.StorageRead>();
var writePermissionsRequest = await Permissions.RequestAsync<Permissions.StorageWrite>();

SpeechToText

We heard your feedback! You now have more control over which Permissions you require and when you request them.

Developers must now manually request permissions for Permissions.Microphone and manually call ISpeechToText.RequestPermissions():

static async Task<bool> ArePermissionsGranted(ISpeechToText speechToText, CancellationToken token = default)
{
    var microphonePermissionStatus = await Permissions.RequestAsync<Permissions.Microphone>();
    var isSpeechToTextRequestPermissionsGranted = await speechToText.RequestPermissions(token);

    return microphonePermissionStatus is PermissionStatus.Granted
            && isSpeechToTextRequestPermissionsGranted;
}

What's Changed

New Contributors

Full Changelog: 13.0.0...14.0.0

Don't miss a new Maui release

NewReleases is sending notifications on new releases.