Question

I'm trying to work with, and understand XAML hierarchy for styles... in simple, a simple Textbox... seen all over the place for how to set the "disabled" background color based on the "IsEnabled" flag. Great, got that.

Now, I want to have another class derived from TextBox... MyTextBox. For this class, I have a property (not dependency property, so I was using DataTrigger). So, I want to keep all the normal TextBox actions that were working, but now get the new trigger to properly update the background color to some other color.. So, here is what I have. just to clarify, all my static resources for colors are SOLID BRUSHES...

<Style TargetType="TextBox" >
  <Setter Property="FontFamily" Value="Courier New" />
  <Setter Property="FontSize" Value="12" />
  <Setter Property="Foreground" Value="{StaticResource MyForeground}" />
  <Setter Property="Background" Value="{StaticResource MyBackground}" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TextBox">
        <Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" 
            BorderBrush="{TemplateBinding BorderBrush}"
            Background="{TemplateBinding Background}" 
            SnapsToDevicePixels="true">

            <ScrollViewer Name="PART_ContentHost" 
                Background="{TemplateBinding Background}" 
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
        </Border>

        <ControlTemplate.Triggers>
          <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Background" Value="{StaticResource MyDisBackground}" />
            <Setter TargetName="PART_ContentHost" Property="Background" 
                 Value="MyDisBackground"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<!--  Now, my derived (or so I was hoping) style that just adds additional trigger -->
<Style TargetType="local:MyTextBox"  BasedOn="{StaticResource {x:Type TextBox}}" >
  <Style.Triggers>
    <DataTrigger Binding="{Binding Path=IsRequired}" Value="True">
      <Setter Property="Background" Value="{StaticResource RequiredBackground}" />
    </DataTrigger>
  </Style.Triggers>
</Style>

Am I missing something simple?

Was it helpful?

Solution

First, your DataTrigger is looking at the DataContext of your MyTextBox (not the control itself). So look at the control, you'd need to do something like:

<DataTrigger Binding="{Binding Path=IsRequired, RelativeSource={RelativeSource Self}}" Value="True">
    <Setter Property="Background" Value="{StaticResource RequiredBackground}" />
</DataTrigger>

Now that will set the MyTextBox.Background property when MyTextBox.IsRequired is true. But dependency property values have a precedence order. So the above will visually change the background used like:

<local:MyTextBox />

In the following case your RequiredBackground brush will not be used. Instead you'll see the MyDisBackground brush:

<local:MyTextBox IsEnabled="False" />

In this case, the ScrollViewer.Background is changed to MyDisBackground and no longer binds to the MyTextBox.Background property. The MyTextBox.Background would still be RequiredBackground, but it's no longer used anywhere.

Finally, in the following case your RequiredBackground brush will not be used.

<local:MyTextBox Background="Yellow" />

Here, the local value (yellow) is at #3 in the precedence list, while the style setter is at #8.

If you make your property a dependency property that defaults to false, then you could do something like:

<Style TargetType="TextBox" >
  <Setter Property="FontFamily" Value="Courier New" />
  <Setter Property="FontSize" Value="12" />
  <Setter Property="Foreground" Value="{StaticResource MyForeground}" />
  <Setter Property="Background" Value="{StaticResource MyBackground}" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="TextBox">
        <Border Name="Bd" BorderThickness="{TemplateBinding BorderThickness}" 
            BorderBrush="{TemplateBinding BorderBrush}"
            Background="{TemplateBinding Background}" 
            SnapsToDevicePixels="true">

            <ScrollViewer Name="PART_ContentHost" 
                Background="{TemplateBinding Background}" 
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
        </Border>

        <ControlTemplate.Triggers>
          <Trigger Property="local:MyTextBox.IsRequired" Value="False">
            <Setter Property="Background" Value="{StaticResource RequiredBackground}" />
            <Setter TargetName="PART_ContentHost" Property="Background" 
                 Value="RequiredBackground"/>
          </Trigger>
          <Trigger Property="IsEnabled" Value="False">
            <Setter Property="Background" Value="{StaticResource MyDisBackground}" />
            <Setter TargetName="PART_ContentHost" Property="Background" 
                 Value="MyDisBackground"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

<Style TargetType="local:MyTextBox"  BasedOn="{StaticResource {x:Type TextBox}}" />

Eventhough the property doesn't exist for TextBox, it can still get the default value of your dependency property and trigger off it. But since it would be set for a TextBox, that trigger will never be used.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top