Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using AppThemeBinding in a IConverter doesn’t react on theme changes

Tags:

maui

.net-maui

I have created a bool to color like this:

public class BoolToColorConverter : BindableObject, IValueConverter
{
    public BoolToColorConverter()
    {
    }

    public static readonly BindableProperty TrueColorProperty =
        BindableProperty.Create(nameof(TrueColor), typeof(Color), typeof(BoolToColorConverter), null, BindingMode.OneWay, null, null);
  
    public Color TrueColor
    {
        get { return (Color) GetValue(TrueColorProperty); }
        set { SetValue(TrueColorProperty, value); }
    }
    
    public Color FalseColor { get; set; } = null!;

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool b && b)
        {
            return TrueColor!;
        }

        return FalseColor!;
    }

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

With this I could use AppThemeBinding when I create the converter:

<ContentPage.Resources>
    <converts:BoolToColorConverter
        x:Key="ColorConverter" 
        TrueColor="{AppThemeBinding Light=DarkRed, Dark=LightSalmon}" 
        FalseColor="#888" />
</ContentPage.Resources>

<VerticalStackLayout>
    <Label
        Text="Hello, World!"
        TextColor="{Binding Source={x:Reference Checky}, Path=IsChecked, Converter={StaticResource ColorConverter}}"
        FontSize="32"
        HorizontalOptions="Center" />

    <CheckBox x:Name="Checky" />
</VerticalStackLayout>

This works as expected if the theme is set on start up.

Dark on start:

enter image description here

Ligh on start:

enter image description here

But if the theme is changed when the application is running, the binding is not reevaluated and the old color is shown. Here is how looks like if thmese is changed from dark mode to light:

enter image description here

Is there some workaround for this?

like image 704
PEK Avatar asked Oct 19 '25 11:10

PEK


1 Answers

You could use the Visual State Manager instead.

With StateTrigger

This is the easiest solution for this specific scenario. Add this converter:

internal class InvertedBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        try
        {
            return !(bool)value;
        }
        catch
        {
            return false;
        }
    }

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

And then use this XAML:

<ContentPage.Resources>
    <converts:InvertedBoolConverter x:Key="InvertedBoolConverter" />
</ContentPage.Resources>

<VerticalStackLayout 
    x:Name="MainLayout"
    Spacing="25" 
    Padding="30,0">

    <Label
    Text="Hello, World!"
    FontSize="32"
    HorizontalOptions="Center">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="Checked state">
                <VisualState Name="Checked">
                    <VisualState.StateTriggers>
                        <StateTrigger 
                            IsActive="{Binding Source={x:Reference Checky}, Path=IsChecked}"  
                        />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="TextColor" Value="{AppThemeBinding Light=DarkRed, Dark=LightSalmon}" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Not checked">
                    <VisualState.StateTriggers>
                        <StateTrigger 
                            IsActive="{Binding Source={x:Reference Checky}, Path=IsChecked, Converter={StaticResource InvertedBoolConverter}}"
                        />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="TextColor" Value="#888" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Label>

    <CheckBox x:Name="Checky" />
</VerticalStackLayout>

With custom trigger

In a more complex scenario, you might want to use a customer trigger instead. Sample:

public class BoolTrigger : StateTriggerBase
{
    public static readonly BindableProperty 
        ValueProperty = BindableProperty.Create(nameof(Value), typeof(object), typeof(BoolTrigger), null, propertyChanged: OnBindablePropertyChanged);

    public object Value
    {
        get => GetValue(ValueProperty);
        set => SetValue(ValueProperty, value);
    }

    public bool OnValue { get; set; }

    private static void OnBindablePropertyChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var trigger = bindable as BoolTrigger;
        trigger.UpdateTrigger();
    }

    private void UpdateTrigger()
    {
        if (Value is not bool val)
        {
            return;
        }

        SetActive(OnValue == val);
    }
}

And then like this in XAML:

<VerticalStackLayout 
    x:Name="MainLayout"
    Spacing="25" 
    Padding="30,0">

    <Label
    Text="Hello, World!"
    FontSize="32"
    HorizontalOptions="Center">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="Checked state">
                <VisualState Name="Checked">
                    <VisualState.StateTriggers>
                        <converts:BoolTrigger 
                            OnValue="true" 
                            Value="{Binding Source={x:Reference Checky}, Path=IsChecked}"  
                        />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="TextColor" Value="{AppThemeBinding Light=DarkRed, Dark=LightSalmon}" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Not checked">
                    <VisualState.StateTriggers>
                        <converts:BoolTrigger 
                            OnValue="false" 
                            Value="{Binding Source={x:Reference Checky}, Path=IsChecked}"  
                        />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Property="TextColor" Value="#888" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Label>

    <CheckBox x:Name="Checky" />
</VerticalStackLayout>
like image 117
PEK Avatar answered Oct 21 '25 12:10

PEK



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!