Search code examples
wpfxamldata-bindingdatatrigger

Data trigger check value of dynamic resource


I have a WPF application and the content of should be displayed in German and English. Therefore, I created two seperate resource dictionaries which contain string snippets in each language. It is possible to switch between the languages while the application is running. At this point I got stuck at a problem. There are some settings the user can make. If the setting was completed successfully a message shows up. The text of the message is taken from the resource dictionary. Based on a success or error message the text is displayed green or red.

<TextBlock Text="{Binding UpdateTaxPercentageSettingsMessage}" FontWeight="Bold">
    <TextBlock.Style>
        <Style TargetType="{x:Type TextBlock}">
            <Style.Triggers>
                <DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{DynamicResource tax_percentage_update_went_wrong}">
                    <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource active_tax_law_update_went_wrong}">
                    <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource tax_percentage_was_updated_successfully}">
                    <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource active_tax_law_was_updated_successfully}">
                    <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource differential_taxation_info_update_went_wrong}">
                    <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource differential_taxation_info_was_updated_successfully}">
                    <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

To make switching languages possible while the application is running, the string snippets taken from the resource dictionary has to be a DynamicResource. Unfortunately, I can not use DynamicResources as condition in data triggers. Has anyone faced a similar problem yet? I am grateful for your suggestions!


Solution

  • Do not use triggers on localized text, neither does it work with DynamicResouce, because Value is not a dependency property, nor is it readable. Instead, create an enum that describes your errors.

    public enum ErrorType
    {
       WhoCares, // No comment on this.
       ThisIsSuspicious, // Suspicous value.
       ItsATrap, // Admiral Ackbar warned us.
       ItIsNotWhatYouThinkItIs, // It is exactly what you think.
       ItCannotBeThatSerious, // Serious Sam approves.
       WhatDoesTheFlashingRedLightMean // If it is still flashing, how bad can it be, really?
    }
    

    Expose anothe property for the error and implement INotifyPropertyChanged if necessary.

    public ErrorType ErrorType { get; } 
    

    Use the property instead of your resources as Value of the triggers.

    <TextBlock Text="{Binding UpdateTaxPercentageSettingsMessage}" FontWeight="Bold">
       <TextBlock.Style>
          <Style TargetType="{x:Type TextBlock}">
             <Style.Triggers>
                <DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.WhoCares}">
                   <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ThisIsSuspicious}">
                   <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ItsATrap}">
                   <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ItIsNotWhatYouThinkItIs}">
                   <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ItCannotBeThatSerious}">
                   <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.WhatDoesTheFlashingRedLightMean}">
                   <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
             </Style.Triggers>
          </Style>
       </TextBlock.Style>
    </TextBlock>
    

    Bonus round. As your data triggers are repetitive (they set the same values often), consider using a Binding with a custom converter that simply says, if the bound value matches any of the given values, apply this setter.

    public class IsMatchingConverter : IValueConverter
    {
       public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
       {
          if (!(parameter is IEnumerable enumerable))
             return false;
    
          return enumerable.Cast<object>().Contains(value);
       }
    
       public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
       {
          throw new InvalidOperationException();
       }
    }
    

    Create an instance of the converter in any resource dictionary in scope.

    <Window.Resources>
       <local:IsMatchingConverter x:Key="IsMatchingConverter"/>
    </Window.Resources>
    

    Change the data triggers and pass the target values in an array as converter parameters.

    <TextBlock Text="{Binding UpdateTaxPercentageSettingsMessage}" FontWeight="Bold">
       <TextBlock.Style>
          <Style TargetType="{x:Type TextBlock}">
             <Style.Triggers>
                <DataTrigger Value="True">
                   <DataTrigger.Binding>
                      <Binding Path="ErrorType" Converter="{StaticResource IsMatchingConverter}">
                         <Binding.ConverterParameter>
                            <x:Array Type="local:ErrorType">
                               <local:ErrorType>WhoCares</local:ErrorType>
                               <local:ErrorType>ThisIsSuspicious</local:ErrorType>
                               <local:ErrorType>ItCannotBeThatSerious</local:ErrorType>
                            </x:Array>
                         </Binding.ConverterParameter>
                      </Binding>
                   </DataTrigger.Binding>
                   <Setter Property="Foreground" Value="Red"/>
                </DataTrigger>
                <DataTrigger Value="True">
                   <DataTrigger.Binding>
                      <Binding Path="ErrorType" Converter="{StaticResource IsMatchingConverter}">
                         <Binding.ConverterParameter>
                            <x:Array Type="local:ErrorType">
                               <local:ErrorType>ItsATrap</local:ErrorType>
                               <local:ErrorType>ItIsNotWhatYouThinkItIs</local:ErrorType>
                               <local:ErrorType>WhatDoesTheFlashingRedLightMean</local:ErrorType>
                            </x:Array>
                         </Binding.ConverterParameter>
                      </Binding>
                   </DataTrigger.Binding>
                   <Setter Property="Foreground" Value="Green"/>
                </DataTrigger>
             </Style.Triggers>
          </Style>
       </TextBlock.Style>
    </TextBlock>