Search code examples
c#wpftextblockdependencyobject

Better way to trigger color change based on displayed text


  1. I have a struct class that records a value and a check, which simply says whether it is good or bad.
    public enum StressCheck
    {
        Good,
        Bad
    }   

    public struct Stress
    {
        public Stress(double stressValue, StressCheck check)
        {
            StressValue = stressValue;
            Check = check;
        }
        public double StressValue { get; set; }               
        public StressCheck Check { get; set; }
    }
  1. I want to create a TextBlock whose background becomes red when the value is 'Bad'. This is what I am doing at the moment in the XAML of my user control.
<UserControl x:Class="ScienceProgram.UserControls.DataCellCheck"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             x:Name="parent">
    <UserControl.Resources>
        <Style x:Key="CheckStress" TargetType="TextBlock">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=TB,Path=Text}" Value="Good">
                    <Setter Property="Background" Value="Green" />
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=TB,Path=Text}" Value="Bad">
                    <Setter Property="Background" Value="Red" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <Grid Margin="1" DataContext="{Binding ElementName=parent}" Width="100">
        <TextBlock Style="{StaticResource CheckStress}" Text="{Binding Path=Value}" />
        <TextBlock x:Name="TB"  Text="{Binding Path=Check}" Visibility="Collapsed"/>
    </Grid>

and the standard code-behind for dependency objects "Value" and "Check"

    public partial class DataCellCheck : UserControl
    {
        public DataCellCheck()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty ValueProp = 
            DependencyProperty.Register("Value", typeof(string), 
                typeof(DataCellCheck), new PropertyMetadata(""));

        public string Value
        {
            get { return GetValue(ValueProp) as String; }
            set { SetValue(ValueProp, value); }
        }

        public static readonly DependencyProperty StatusProp = 
            DependencyProperty.Register("Check", typeof(string), 
                typeof(DataCellCheck), new PropertyMetadata(""));

        public string Check
        {
            get { return GetValue(StatusProp) as String; }
            set { SetValue(StatusProp, value); }
        }
    }
  1. So, what's happening here is that I am changing the Background color of my displayed TextBlock by binding it to a collapsed Texblock, in which I bind the StressCheck value from the viewModel in the following manner.
    public class TestViewModel : BindableBase
    {
        public Stress MyFirstStress { get; set; }

        public TestViewModel()
        {
            MyFirstStress = new Stress(1245, StressCheck.Fail);
        }

        public double DisplayStressValue => MyFirstStress.StressValue;
        public string DisplayStressCheck => MyFirstStress.Check.ToString();
    }
        

and in XAML

<UserControl x:Class="ScienceProgram.Views.TestView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ScienceProgram.Views"
             xmlns:uc="clr-namespace:ScienceProgram.UserControls"
             mc:Ignorable="d" 
             d:DesignHeight="1200" d:DesignWidth="811">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="65"/>
        </Grid.RowDefinitions>
        <StackPanel>
            <uc:DataCellCheck Value="{Binding Path=DisplayStressValue }" Check="{Binding Path=DisplayStressCheck}"/>
        </StackPanel>
    </Grid>
</UserControl>
  1. Now, my question is there a better way to trigger the color change in the displayed textblock, without having to bind StressCheck and hide it in another textblock?

Many many thanks.


Solution

  • is there a better way to trigger the color change in the displayed textblock, without having to bind StressCheck and hide it in another textblock?

    I should hope so. The way you're doing it now is very kludgy.

    Without a Minimal, Complete, Verifiable code example, it's impossible to confirm that this would work in your exact scenario, but it should. You can just bind to the Check property in the trigger instead of the other TextBlock:

    <Style x:Key="CheckStress" TargetType="TextBlock">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Check}" Value="Good">
                <Setter Property="Background" Value="Green" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Check}" Value="Bad">
                <Setter Property="Background" Value="Red" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
    

    For what it's worth, I prefer to include a setter for one value and use triggers for the other (or others, if there are multiple):

    <Style x:Key="CheckStress" TargetType="TextBlock">
        <Setter Property="Background" Value="Green"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding Check}" Value="Bad">
                <Setter Property="Background" Value="Red" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
    

    Either way, WPF handles enum types implicitly, parsing a provided text value as the actual enum value and using it for the trigger's comparison. You don't need the extra TextBlock or other mechanism (such as a converter) to translate between the text you put in the XAML and the actual enum values.

    If the above doesn't address your problem, please improve the question by providing a good MCVE so that the full context of your scenario can be understood.