Search code examples
wpfvalidationxamlbindingerrortemplate

Validation.ErrorTemplate that accesses properties on the AdornedElementPlaceholder


I would like to have my error template look different depending on some property values on the adorned control.

Setting the TargetType like below results in a runtime exception: 'TextBox' ControlTemplate TargetType does not match templated type 'Control'. Thus, it appears that the ErrorTemplate must use a targetType of 'Control'.

<ControlTemplate x:Key="ValidationErrorTemplate" TargetType={x:Type TextBox}>
  <Grid>
    <AdornedElementPlaceholder  HorizontalAlignment="Left" Name="placeholder"/>
    <Grid Background="Yellow">
      <Grid.Style>
        <Style TargetType="Grid">
          <Style.Triggers>
            <DataTrigger Binding="{TemplateBinding IsReadOnly}" Value="True">
              <Setter Property="Background" Value="Green"/>
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </Grid.Style>
    </Grid>
  </Grid>
</ControlTemplate>

I removed the targetType and then tried this:

<DataTrigger Binding="{Binding IsReadOnly, RelativeSource={RelativeSource AncestorType={x:Type TextBox}}}" Value="True">
  <Setter Property="Background" Value="Green"/>
</DataTrigger>

And then this, which yielded no exceptions but also no effect:

<DataTrigger Binding="{Binding AdornedElement.(TextBox.IsReadOnly), ElementName=placeholder}" Value="True">
  <Setter Property="Background" Value="Orange"/>
</DataTrigger>

and this, which yielded no exceptions but also no effect:

<DataTrigger Binding="{Binding (TextBox.IsReadOnly), ElementName=placeholder}" Value="True">
  <Setter Property="Background" Value="Orange"/>
</DataTrigger>

and finally this, which yielded "BindingExpression path error: 'IsReadOnly' property not found on 'object' ''AdornedElementPlaceholder'":

<DataTrigger Binding="{Binding IsReadOnly, ElementName=placeholder}" Value="True">
  <Setter Property="Background" Value="Green"/>
</DataTrigger>

Does anyone have any other ideas on how to reference dependency properties in the ErrorTemplate?


Solution

  • The correct answer was:

    <DataTrigger Binding="{Binding AdornedElement.(TextBox.IsReadOnly), ElementName=placeholder}" Value="True">
      <Setter Property="Background" Value="Orange"/>
    </DataTrigger>
    

    While this was one of my failed attempts early on, my test setup was flawed. I was setting the default background property on the grid instead of setting it in the style. Due to Dependency Property precedence, the value set directly on the object will always trump any value set in a style (specifically, in my triggers).

    Here is a working setup:

    <ControlTemplate x:Key="ValidationErrorTemplate">
      <Grid>
        <Grid.Style>
          <Style TargetType="{x:Type Grid}">
            <Setter Property="Background" Value="Yellow"/>
            <Style.Triggers>
              <DataTrigger Binding="{Binding AdornedElement.(TextBox.IsReadOnly), ElementName=placeholder}" Value="True">
                <Setter Property="Background" Value="Orange"/>
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </Grid.Style>
    
        <AdornedElementPlaceholder Name="placeholder"/>
      </Grid>
    </ControlTemplate>
    

    One key here is that AdornedElement is always of type Control, so you must do the appropriate qualification (or cast?) to access properties that are not exposed on Control. This is done via the parenthesis around the class name and property. Another example is: AdornedElement.(CheckBox.IsChecked). Since IsChecked is not on Control, you must qualify it by explicitly stating the class type that owns the property.