Search code examples
wpfstylesconvertersdatatrigger

WPF One DataTrigger with multiple Values


I have a Slider and a TextBlock on my WPF window. The TextBlock needs to change the background, foreground and font size by the value of the slider, by rang.

I built a converter that receives the value of the slider and returns a 0, 1 or 3 for each group.

public class ValueByRange : IValueConverter
{
    public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double.TryParse(value?.ToString(), out double dValue);

        if (dValue > 80)
            return 2;
        else if (dValue > 50)
            return 1;

        return 0;
    }
    public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return false;
    }
}

My TextBlock looks like this:

<TextBlock Width="30" Text="{Binding ElementName= theSlider, Path=Value}">
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="Foreground" Value="Black"/>
                <Setter Property="FontSize" Value="12"/>
                <Setter Property="TextAlignment" Value="Center"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ElementName=theSlider, Path=Value, Converter={StaticResource ValueByRange}}" Value="1">
                        <Setter Property="Background" Value="Yellow"/>
                        <Setter Property="Foreground" Value="Black"/>
                        <Setter Property="FontSize" Value="14"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding ElementName=theSlider, Path=Value, Converter={StaticResource ValueByRange}}" Value="2">
                        <Setter Property="Background" Value="Red"/>
                        <Setter Property="Foreground" Value="White"/>
                        <Setter Property="FontSize" Value="16"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
</TextBlock>

It's working fine, but it doesn't feel like the best approach. The converter fires 2 time, each for every DataTrigger. I need more than this 3 ranges and that means the converter will fire more times. It's also not helpful to build a converter for each property, for the same reason.

Is there a way to fire the converter only ones and then check the result (without using code beyond)?

I know this is not the correct syntax, but I mean something like this:

<DataTrigger Binding="{Binding ElementName=theSlider, Path=Value, Converter={StaticResource ValueByRange}}">
    <DataTrigger.Value Value ="1">
        <Setter Property="Background" Value="Yellow" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="FontSize" Value="14" />
    </DataTrigger.Value>
    <DataTrigger.Value Value ="2">
        <Setter Property="Background" Value="Red" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontSize" Value="16" />
    </DataTrigger.Value>
</DataTrigger>

Solution

  • Well, as Clemens said, there's nothing wrong with having a converter called multiple times.

    But if you really want to do it, here's an approach that works:

    <Grid>
        <Grid.Resources>
    
            <local:ValueByRange x:Key="ValueByRange" />
    
            <Style x:Key="TextBlockInLabelStyle" TargetType="TextBlock">
                <Setter Property="Background" Value="Transparent"/>
                <Setter Property="Foreground" Value="Black"/>
                <Setter Property="FontSize" Value="12"/>
                <Setter Property="TextAlignment" Value="Center"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding}" Value="1">
                        <Setter Property="Background" Value="Yellow"/>
                        <Setter Property="Foreground" Value="Black"/>
                        <Setter Property="FontSize" Value="14"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding}" Value="2">
                        <Setter Property="Background" Value="Red"/>
                        <Setter Property="Foreground" Value="White"/>
                        <Setter Property="FontSize" Value="16"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
    
        </Grid.Resources>
    
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
    
        <Slider Grid.Row="0"
                x:Name="Slider1" 
                Interval="1" 
                Minimum="0" 
                Maximum="100" />
    
        <Slider Grid.Row="1" 
                x:Name="Slider2" 
                Interval="1" 
                Minimum="0" 
                Maximum="100" />
    
        <Label Grid.Row="2" Content="{Binding ElementName=Slider1, Path=Value, Converter={StaticResource ValueByRange}}">
            <Label.Resources>
                <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockInLabelStyle}" />
            </Label.Resources>
        </Label>
    
        <Label Grid.Row="3" Content="{Binding ElementName=Slider2, Path=Value, Converter={StaticResource ValueByRange}}">
            <Label.Resources>
                <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource TextBlockInLabelStyle}" />
            </Label.Resources>
        </Label>
    </Grid>