Search code examples
wpftriggersbinding

Wpf Set background color of control based on build


I have a combo box that I'm setting the background color by editing the template. The template is in a resource dictionary file. I have two different build configurations and would like to set the color of the background based on which build it is. My idea is to bind the background in the style to a Tag or something and set the color in the code behind. How is this accomplished? Is there a better way to do this?

MainWindow.xaml

<Grid>
    <ComboBox ItemsSource="{Binding ExpenseCategories}" Style="{StaticResource ComboBoxStyleGrayPurple}" Background="{Binding ComboBoxStaticBackground}" SelectedIndex="{Binding SelectedExcatIndex}" Width="150" HorizontalAlignment="Left" />
</Grid>

ResourceDictionary.xaml

These are the relevant lines in the edited template:

    <LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
        <GradientStop Color="#f8f1f9" Offset="0.0"/>
        <GradientStop Color="#e4c9e7" Offset="1.0"/>
    </LinearGradientBrush>

    <ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
        <Grid x:Name="templateRoot" SnapsToDevicePixels="true">
            <Grid.ColumnDefinitions>
            ...
            </Grid.ColumnDefinitions>
            <Popup 
            ...
            </Popup>
            <ToggleButton x:Name="toggleButton" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
            <ContentPresenter .../>
        </Grid>
        <ControlTemplate.Triggers>
         ...
        </ControlTemplate.Triggers>
    </ControlTemplate>

    <Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ToggleButton}">
                    <Border x:Name="templateRoot" Background="{StaticResource ComboBox.Static.Background}" BorderBrush="{StaticResource ComboBox.Static.Border}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
                        <Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="true" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
                            <Path x:Name="arrow" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z" Fill="{StaticResource ComboBox.Static.Glyph}" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center"/>
                        </Border>
                    </Border>
                    <ControlTemplate.Triggers>
                      ...
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

My idea is to modify the line in the ComboBoxToggleButton -> Border -> Background to bind to something in the ComboBox in MainWndow.xaml which then is set in the code behind, but I'm not quite sure how to do this.

MainWindowViewModel.cs

void SetValue()
{
#if BUILD2
    LinearGradientBrush build2Brush = new LinearGradientBrush(); // set build2 color
#else
    LinearGradientBrush build1Brush = new LinearGradientBrush(); // set build1 color
#endif
}

How is this best accomplished?


Solution

  • You must not handle such layout responsibilities in the view model.
    Instead, move it to code-behind (or to the view in general).

    You should also define the Color instances and not the Brush (for convenience when configuring and referencing the brushes in XAML).

    The following example creates a code-behind file for the ResourceDictionary that defines the resources. To accomplish this, we have to extend ResourceDictionary. Crucial to this is the partial keyword in the class declaration and the x:Class attribute on the XAML root element:

    MyResources.xaml

    namespace Example
    {
      using System.Windows;
      using System.Windows.Media;
    
      public partial class MyResources : ResourceDictionary
      {
    #if DEBUG
        public static Color ConditionalGradientStopColor1 { get; } = (Color)ColorConverter.ConvertFromString("#f8f1f9");
        public static Color ConditionalGradientStopColor2 { get; } = (Color)ColorConverter.ConvertFromString("#e4c9e7");
    #else
        public static Color ConditionalGradientStopColor1 { get; } = Colors.LightBlue;
        public static Color ConditionalGradientStopColor2 { get; } = Colors.DarkBlue;
    #endif
      }
    }
    

    MyResources.xaml

    <ResourceDictionary x:Class="Example.MyResources"
                        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                        xmlns:local="clr-namespace:Example">
    
      <LinearGradientBrush x:Key="ComboBox.Static.Background"
                           EndPoint="0,1"
                           StartPoint="0,0">
        <GradientStop Color="{x:Static local:MyResources.ConditionalGradientStopColor1}"
                      Offset="0.0" />
        <GradientStop Color="{x:Static local:MyResources.ConditionalGradientStopColor2}"
                      Offset="1.0" />
      </LinearGradientBrush>
    
      <Style x:Key="TextBoxStyle" TargetType="TextBox">
        <Setter Property="Background"
                Value="{StaticResource ComboBox.Static.Background}" />
      </Style>
    </ResourceDictionary>
    

    Then use everything as usual:

    MainWindow.xaml

    <Window>
      <Window.Resources>
        <ResourceDictionary>
          <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/MyResources.xaml" />
          </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
      </Window.Resources>
    
      <TextBox Style={StaticResource TextBoxStyle}" />
    </Window>