Building Dynamic UI with Decision Logic in .NET MAUI XAML

Building Dynamic UI with Decision Logic in .NET MAUI XAML

27 January 2025

Buy Me A Coffee

Hello and welcome to the 2025 journey of .NET MAUI!

In this article, we will explore how to build dynamic user interfaces with decision logic in .NET MAUI using XAML.

Dynamic UIs are essential for creating responsive and engaging applications that adapt to user input, application state, or external conditions. By leveraging data bindings, triggers, converters, and behaviors, you can implement dynamic UI elements that enhance the user experience and improve the efficiency of your .NET MAUI applications.

Out of the box concepts for Dynamic UI in .NET MAUI

1. Data Binding

Data binding connects UI elements to data sources, enabling automatic updates when data changes. Use the {Binding} syntax to bind properties in XAML.

Example:

<Label Text="{Binding UserName}" FontSize="20" />

2. Triggers

Triggers let you change UI properties based on conditions.

Example:

<Entry Text="{Binding UserInput}">
    <Entry.Triggers>
        <DataTrigger TargetType="Entry" Binding="{Binding UserInput}" Value="">
            <Setter Property="Placeholder" Value="Please enter your name" />
        </DataTrigger>
    </Entry.Triggers>
</Entry>

3. Value Converters

Converters allow you to transform data from one format to another before displaying it.

Example:

public class BoolToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Colors.Green : Colors.Red;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

XAML Usage:

<Label Text="Status" TextColor="{Binding IsOnline, Converter={StaticResource BoolToColorConverter}}" />

4. Behaviors

Behaviors add reusable, event-driven functionality to controls.

Example:

public class FocusBehavior : Behavior<Entry>
{
    protected override void OnAttachedTo(Entry bindable)
    {
        base.OnAttachedTo(bindable);
        bindable.Focused += OnFocused;
    }

    protected override void OnDetachingFrom(Entry bindable)
    {
        base.OnDetachingFrom(bindable);
        bindable.Focused -= OnFocused;
    }

    private void OnFocused(object sender, FocusEventArgs e)
    {
        ((Entry)sender).Text = "";
    }
}

XAML Usage:

<Entry Text="Enter text">
    <Entry.Behaviors>
        <local:FocusBehavior />
    </Entry.Behaviors>
</Entry>

But sometimes you want to add If and Switch conditions directly in your XAML for more dynamic UI capabilities. This makes it easier to handle conditional rendering without relying heavily on code-behind logic.

Let's create a ConditionView and SwitchCaseView that allow you to define If and Switch conditions in XAML.

ConditionView

The ConditionView control renders its content based on a condition.

public class ConditionView : ContentView
{
	public static readonly BindableProperty FalseProperty = BindableProperty.Create(nameof(False), typeof(View), typeof(ConditionView), default(View));
	public static readonly BindableProperty TrueProperty = BindableProperty.Create(nameof(True), typeof(View), typeof(ConditionView), default(View));
	public static readonly BindableProperty IfProperty = BindableProperty.Create(nameof(If), typeof(bool), typeof(ConditionView), default(bool), propertyChanged: ConditionChanged);

	private static void ConditionChanged(BindableObject bindable, object oldvalue, object newvalue)
	{
		var conditionView = (ConditionView)bindable;
		conditionView.Content = (bool)newvalue ? conditionView.True : conditionView.False;
	}

	public bool If
	{
		get => (bool)GetValue(IfProperty);
		set => SetValue(IfProperty, value);
	}

	public View True
	{
		get => (View)GetValue(TrueProperty);
		set => SetValue(TrueProperty, value);
	}

	public View False
	{
		get => (View)GetValue(FalseProperty);
		set => SetValue(FalseProperty, value);
	}
}

XAML Usage:

<mauiConditionView:ConditionView If="{Binding Condition}">
	<mauiConditionView:ConditionView.True>
		<Label Text="Condition is TRUE" />
	</mauiConditionView:ConditionView.True>
	<mauiConditionView:ConditionView.False>
		<Label Text="Condition is FALSE" />
	</mauiConditionView:ConditionView.False>
</mauiConditionView:ConditionView>

In this example, the rendered content will depend on Condition flag.

Switch Case View

The SwitchCaseView control renders content based on multiple cases.

public class SwitchCaseView<T> : ContentView
	where T : notnull
{
	public static readonly BindableProperty CasesProperty = BindableProperty.Create(nameof(Cases), typeof(ICollection<CaseView<T>>), typeof(ConditionView), new List<CaseView<T>>(), propertyChanged:SwitchChanged);
	public static readonly BindableProperty DefaultProperty = BindableProperty.Create(nameof(Default), typeof(View), typeof(ConditionView), propertyChanged: SwitchChanged);
	public static readonly BindableProperty SwitchProperty = BindableProperty.Create(nameof(Switch), typeof(T), typeof(ConditionView), propertyChanged: SwitchChanged);

	private static void SwitchChanged(BindableObject bindable, object oldvalue, object newvalue)
	{
		var switchCaseView = (SwitchCaseView<T>)bindable;
		switchCaseView.Content = switchCaseView.Cases
		                                     .Where(x => x.Case.Equals(switchCaseView.Switch))
		                                     .Select(x => x.Content)
		                                     .SingleOrDefault(switchCaseView.Default);
	}

	public T Switch
	{
		get => (T)GetValue(SwitchProperty);
		set => SetValue(SwitchProperty, value);
	}

	public View? Default
	{
		get => (View?)GetValue(DefaultProperty);
		set => SetValue(DefaultProperty, value);
	}

	public ICollection<CaseView<T>> Cases
	{
		get => (ICollection<CaseView<T>>)GetValue(CasesProperty);
		set => SetValue(CasesProperty, value);
	}
}

public class CaseView<T> : ContentView
{
	public static readonly BindableProperty CaseProperty = BindableProperty.Create(nameof(Case), typeof(T), typeof(CaseView<T>));
	public T Case
	{
		get => (T)GetValue(CaseProperty);
		set => SetValue(CaseProperty, value);
	}
}

XAML Usage:

<mauiConditionView:SwitchCaseView x:TypeArguments="system:Int32" Switch="{Binding Case}">
	<mauiConditionView:SwitchCaseView.Cases>
		<mauiConditionView:CaseView x:TypeArguments="system:Int32" Case="1">
			<Label Text="Case 1" />
		</mauiConditionView:CaseView>
		<mauiConditionView:CaseView x:TypeArguments="system:Int32" Case="3">
			<Label Text="Case 3" />
		</mauiConditionView:CaseView>
	</mauiConditionView:SwitchCaseView.Cases>
	<mauiConditionView:SwitchCaseView.Default>
		<Label Text="Case Default" />
	</mauiConditionView:SwitchCaseView.Default>
</mauiConditionView:SwitchCaseView>

This SwitchCaseView will display a different Label depending on the Case value.

Conclusion

Dynamic UI in .NET MAUI XAML empowers developers to create responsive, user-centric applications with minimal code. By combining data bindings, triggers, converters, behaviors, and controls like ConditionView and SwitchCaseView, you can handle complex UI requirements efficiently. Start building dynamic UIs today and enhance your app's interactivity and usability!

Buy Me A Coffee

Related:

Converting Word documents to PDF on mobile devices

Explore the performance and challenges of converting Word documents to PDF on mobile devices using .NET MAUI, comparing libraries like Aspose.Words, Syncfusion DocIO, and Spire.Doc.

State Desynchronization between .NET and JavaScript

Explore the performance and challenges of state desynchronization between C# and JavaScript in .NET MAUI Blazor applications.

An unhandled error has occurred. Reload

🗙