[FIXED] Dynamic styling in .Net Maui/Xamarin

Issue

Let’s say I want to display a calendar. Each day (DayViewModel) can have such attributes that should influence styling:

  • is day of current month
  • is weekend
  • is day available

And based on that I’d like to style my DayContentView like follows:

  • if day of current month -> opacity 1
  • if not current month -> opacity 0.5
  • if weekend -> background color is red
  • if available -> background color is green

Moreover the last one property ("is day available") can change based on user’s action (clicked button "Check availability").

In other frameworks I’d create a few styles/style classes and assign them accordingly base on those flags. But since StyleClass is not bindable I cannot. Can I? Maybe there is some workaround.

Another option could be to create a separate style (using inheritance if needed) for all the combinations. But first of all the number of combinations raises like 2^n and the second issue is that I have no idea how to dynamically change whole styl for a view changing its name. Is it possible?

Finishing my quite long question: How to do it right/in a smart way? I don’t want to store values of colors, font sizes, opacities etc. in a view model and bind all the values manually.

Solution

The same thing can be achieved using DataTriggers.

Result:

result

MainPage.xaml

<Window
    x:Class="WpfApp6.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:WpfApp6"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="800"
    Height="450"
    mc:Ignorable="d">

    <Window.DataContext>
        <local:MainViewModel />
    </Window.DataContext>

    <ItemsControl Margin="5" ItemsSource="{Binding Days}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type local:DayModel}">
                <Border
                    Padding="10"
                    Width="200"
                    Height="100"
                    Margin="5">
                    <Border.Style>
                        <Style TargetType="Border">
                            <Setter Property="Background" Value="Gray" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsDayOfCurrentMonth}" Value="False">
                                    <Setter Property="Opacity" Value="0.5" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding IsAvailable}" Value="True">
                                    <Setter Property="Background" Value="Green" />
                                </DataTrigger>
                                <DataTrigger Binding="{Binding IsWeekend}" Value="True">
                                    <Setter Property="Background" Value="Red" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Border.Style>
                    <StackPanel>
                        <TextBlock Text="{Binding IsDayOfCurrentMonth, StringFormat='IsDayOfCurrentMonth: {0}'}" />
                        <TextBlock Text="{Binding IsAvailable, StringFormat='IsAvailable: {0}'}" />
                        <TextBlock Text="{Binding IsWeekend, StringFormat='IsWeekend: {0}'}" />
                    </StackPanel>
                </Border>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

DayModel.cs

public record DayModel(bool IsDayOfCurrentMonth, bool IsWeekend, bool IsAvailable);

MainViewModel.cs

public class MainViewModel
{
    public ObservableCollection<DayModel> Days { get; } = new();

    public MainViewModel()
    {
        //Create all possible cominations of days
        for (int i = 0; i < 2; i++)
        {
            for (int j = 0; j < 2; j++)
            {
                for (int k = 0; k < 2; k++)
                {
                    Days.Add(new DayModel(i == 0, j == 0, k == 0));
                }
            }
        }
    }
}

EDIT: sharing style by multiple controls

Here’s how the same Style with Triggers might be shared by multiple controls (two of the three TextBlocks in this example):

<DataTemplate DataType="{x:Type local:DayModel}">
    <Border
        Width="200"
        Height="100"
        Margin="5"
        Padding="10"
        Background="#eee">
        <StackPanel>
            <StackPanel.Resources>
                <Style x:Key="ColoredTextBlock" TargetType="TextBlock">
                    <Setter Property="Foreground" Value="Gray" />
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding IsDayOfCurrentMonth}" Value="False">
                            <Setter Property="Opacity" Value="0.5" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsAvailable}" Value="True">
                            <Setter Property="Foreground" Value="Green" />
                        </DataTrigger>
                        <DataTrigger Binding="{Binding IsWeekend}" Value="True">
                            <Setter Property="Foreground" Value="Red" />
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </StackPanel.Resources>
            <TextBlock Style="{StaticResource ColoredTextBlock}" Text="{Binding IsDayOfCurrentMonth, StringFormat='IsDayOfCurrentMonth: {0}'}" />
            <TextBlock Style="{StaticResource ColoredTextBlock}" Text="{Binding IsAvailable, StringFormat='IsAvailable: {0}'}" />
            <TextBlock Text="{Binding IsWeekend, StringFormat='IsWeekend: {0}'}" />
        </StackPanel>
    </Border>
</DataTemplate>

The result looks like this:
sharing style example result

Answered By – Michal Diviš

Answer Checked By – Katrina (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published