Custom Controls and Bindable Properties in .NET MAUI
Custom Controls and Bindable Properties in .NET MAUI
Building reusable, flexible, and dynamic UI components
Custom controls are essential for building reusable, consistent UI components in .NET MAUI. They help reduce code duplication and create a maintainable UI architecture. Understanding bindable properties is key to making these controls flexible and data-driven [citation:5][citation:12].
⚡ Key Insight: Custom controls with bindable properties are the building blocks of professional, reusable UI components in .NET MAUI [citation:5].
Creating a Custom Control
A custom control is a class that inherits from ContentView or an existing control. It combines multiple controls into a single reusable component [citation:5].
public class FileUploadControl : ContentView
{
private readonly Button _uploadButton;
private readonly Label _fileNameLabel;
private readonly ProgressBar _progressBar;
public FileUploadControl()
{
_uploadButton = new Button { Text = "Choose File" };
_fileNameLabel = new Label { Text = "No file selected" };
_progressBar = new ProgressBar { Progress = 0 };
var layout = new VerticalStackLayout
{
Spacing = 8,
Children = { _uploadButton, _fileNameLabel, _progressBar }
};
Content = layout;
}
}
Adding Bindable Properties
Bindable properties allow your custom control to participate in data binding. They enable property values to be set through XAML and data binding, making your control flexible and reusable [citation:5].
public class FileUploadControl : ContentView
{
// Bindable property for the file name
public static readonly BindableProperty FileNameProperty =
BindableProperty.Create(
nameof(FileName),
typeof(string),
typeof(FileUploadControl),
defaultValue: string.Empty,
defaultBindingMode: BindingMode.TwoWay);
public string FileName
{
get => (string)GetValue(FileNameProperty);
set => SetValue(FileNameProperty, value);
}
// Bindable property for upload progress
public static readonly BindableProperty UploadProgressProperty =
BindableProperty.Create(
nameof(UploadProgress),
typeof(double),
typeof(FileUploadControl),
defaultValue: 0.0,
propertyChanged: OnUploadProgressChanged);
public double UploadProgress
{
get => (double)GetValue(UploadProgressProperty);
set => SetValue(UploadProgressProperty, value);
}
// Command for upload action
public static readonly BindableProperty UploadCommandProperty =
BindableProperty.Create(
nameof(UploadCommand),
typeof(ICommand),
typeof(FileUploadControl));
public ICommand UploadCommand
{
get => (ICommand)GetValue(UploadCommandProperty);
set => SetValue(UploadCommandProperty, value);
}
private static void OnUploadProgressChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (FileUploadControl)bindable;
if (control._progressBar != null)
{
control._progressBar.Progress = (double)newValue;
}
}
}
Using the Custom Control in XAML
<!-- Import the namespace -->
<ContentPage xmlns:controls="clr-namespace:ACUAMITCA.Controls">
<VerticalStackLayout Padding="20">
<controls:FileUploadControl
FileName="{Binding SelectedFileName}"
UploadProgress="{Binding UploadProgress}"
UploadCommand="{Binding UploadFileCommand}" />
</VerticalStackLayout>
</ContentPage>
Advanced: Attached Properties and Behaviors
Attached properties extend the functionality of existing controls without subclassing them. They are similar to bindable properties but are defined in a separate class [citation:5].
public static class RoleManager
{
public static readonly BindableProperty ShowForRolesProperty =
BindableProperty.CreateAttached(
"ShowForRoles",
typeof(string),
typeof(RoleManager),
defaultValue: null,
propertyChanged: OnShowForRolesChanged);
public static string GetShowForRoles(BindableObject view)
{
return (string)view.GetValue(ShowForRolesProperty);
}
public static void SetShowForRoles(BindableObject view, string value)
{
view.SetValue(ShowForRolesProperty, value);
}
private static void OnShowForRolesChanged(BindableObject bindable, object oldValue, object newValue)
{
var view = (View)bindable;
var roles = (string)newValue;
view.IsVisible = CurrentUser.HasRole(roles);
}
}
<!-- Using the attached property -->
<Button Text="Admin Only" local:RoleManager.ShowForRoles="Admin,SuperAdmin" />
Attached Behaviors
Attached behaviors are similar to attached properties but encapsulate reusable UI logic, such as animations or validation [citation:5].
public class TextValidationBehavior : Behavior<Entry>
{
protected override void OnAttachedTo(Entry entry)
{
base.OnAttachedTo(entry);
entry.TextChanged += OnTextChanged;
}
protected override void OnDetachingFrom(Entry entry)
{
base.OnDetachingFrom(entry);
entry.TextChanged -= OnTextChanged;
}
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
var entry = (Entry)sender;
entry.BackgroundColor = string.IsNullOrWhiteSpace(e.NewTextValue)
? Colors.Red
: Colors.Transparent;
}
}
Best Practices
✅ Use Bindable Properties
Always define properties as bindable to support data binding.
✅ Provide Default Values
Set sensible default values for your bindable properties.
✅ Handle Property Changes
Use propertyChanged callbacks to update the UI when properties change.
✅ Keep Controls Focused
Each custom control should have a single, well-defined responsibility.
Conclusion
📌 Key Takeaway: Custom controls with bindable properties are essential for creating reusable, maintainable UI components. Attached properties and behaviors extend this flexibility to existing controls [citation:5][citation:12].