When creating custom controls that use databinding in n .NET MAUI it’s useful to create custom bindable properties. Custom controls can participate in XAML based data binding like any other UI control using Bindable properties.
Custom Bindable properties overview
A bindable property allows you to:
- Bind UI to data that expose.
- Let the UI know if a change has taken place on a property.
- They can participate in property value inheritance, and be made more dynamic.
With the BindableProperty class we create a bindable property which lets us define the property with extra properties such as defaults values and property changed callbacks and validations.
So I’m going to make a custom control called RatingView. The binding to the current rating value via a bindable property called Rating will be done through this control.
So first let’s create a RatingView that will contain star ratings and use a custom bindable property ‘Rating’ that will be implemented dynamically.
Creating the Custom Control
Next, we lay out the control in the new ContentView, RatingView.
RatingView.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAppExample.RatingView">
<StackLayout Orientation="Horizontal" Spacing="5">
<Image x:Name="Star1" Source="star_empty.png" WidthRequest="40" HeightRequest="40" />
<Image x:Name="Star2" Source="star_empty.png" WidthRequest="40" HeightRequest="40" />
<Image x:Name="Star3" Source="star_empty.png" WidthRequest="40" HeightRequest="40" />
<Image x:Name="Star4" Source="star_empty.png" WidthRequest="40" HeightRequest="40" />
<Image x:Name="Star5" Source="star_empty.png" WidthRequest="40" HeightRequest="40" />
</StackLayout>
</ContentView>
RatingView.xaml.cs
using Microsoft.Maui.Controls;
namespace MauiAppExample
{
public partial class RatingView : ContentView
{
// Define a bindable property named Rating
public static readonly BindableProperty RatingProperty = BindableProperty.Create(
propertyName: nameof(Rating),
returnType: typeof(int),
declaringType: typeof(RatingView),
defaultValue: 0,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: OnRatingPropertyChanged);
public int Rating
{
get => (int)GetValue(RatingProperty);
set => SetValue(RatingProperty, value);
}
public RatingView()
{
InitializeComponent();
UpdateStars();
}
// Callback for when the Rating property changes
private static void OnRatingPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (RatingView)bindable;
control.UpdateStars();
}
// Method to update the visual state of stars based on the Rating
private void UpdateStars()
{
Star1.Source = Rating >= 1 ? "star_filled.png" : "star_empty.png";
Star2.Source = Rating >= 2 ? "star_filled.png" : "star_empty.png";
Star3.Source = Rating >= 3 ? "star_filled.png" : "star_empty.png";
Star4.Source = Rating >= 4 ? "star_filled.png" : "star_empty.png";
Star5.Source = Rating >= 5 ? "star_filled.png" : "star_empty.png";
}
}
}
Define Bindable Property (RatingProperty
):
BindableProperty.Create
:propertyName
: The property (Rating) name.returnType
: A property of type int.declaringType
: This property belongs to the Class (RatingView).defaultValue
: Contains (by default) the value 0 for the property.defaultBindingMode
: Sets how changes are propagated (TwoWay for read and write).propertyChanged
: This is a callback method (OnRatingPropertyChanged) triggered every time the property value changes.
Rating Property:
- GetValue and SetValue are used by the property to talk to BindableProperty.
- It gives Rating property to use in both XAML or in data binding.
Property Changed Callback (OnRatingPropertyChanged
):
- UpdateStars is called whenever Rating changes and the UI needs to be updated.
Update Stars Method:
- The source images for each star gets updated with their Rating value (star_filled.png or star_empty.png).
Using the Custom Control with Data Binding
So, let’s use the RatingView
in a page and bind it to a ViewModel property.
MainViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace MauiAppExample
{
public class MainViewModel : INotifyPropertyChanged
{
private int _userRating;
public int UserRating
{
get => _userRating;
set
{
if (_userRating != value)
{
_userRating = value;
OnPropertyChanged();
}
}
}
public MainViewModel()
{
UserRating = 3; // Default rating
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
UserRating
Property: This property implements the Rating property of RatingView control.- While this will notify the UI when the rating changes, to do this using INotifyPropertyChanged.
XAML for Main Page (MainPage.xaml
):
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiAppExample.MainPage"
xmlns:local="clr-namespace:MauiAppExample">
<ContentPage.BindingContext>
<local:MainViewModel />
</ContentPage.BindingContext>
<StackLayout Padding="20" Spacing="30" VerticalOptions="Center">
<Label Text="Please rate the service:" FontSize="Large" HorizontalOptions="Center" />
<!-- Using the custom RatingView control -->
<local:RatingView Rating="{Binding UserRating, Mode=TwoWay}" />
<Label Text="{Binding UserRating, StringFormat='Your rating: {0} stars'}"
FontSize="Medium"
HorizontalOptions="Center" />
</StackLayout>
</ContentPage>
BindingContext
:
- MainViewModel is added as BindingContext of MainPage.
RatingView
Binding:
- UserRating is two way bound to the Rating property of the ViewModel. Any change to the UI will be sent to the ViewModel and the ViewModel will send a change to the UI.
Label
Binding:
- UserRating is bound property that displays the current rating using a label, formatted with StringFormat.
Some benefits of Custom Bindable Properties.
- Reusable Custom Controls: The ability to create bindable properties bonds your UI behavior in a neat, reusable package, which makes it possible to create custom controls that are reusable across multiple pages.
- Data Binding Support: Custom bindable properties use the XAML data binding system, allowing powerful data driven UIs.
- Ease of Maintenance: This makes it easier to maintain and extend custom controls as your application grows because you have properties exposed as bindable properties.