Search code examples
wpfbindingtriggersadorner

AdornedElement Properties in a Trigger


I have an Adorner in XAML that I'm using for ErrorValidation. Basically I have a grid that I want to display on two conditions (if the "AdornedElement" IsFocused or IsMouseOver).

Below is a code snippet where I'm binding - successfully - to the IsFocused of the AdornedElement, but as you can tell that only solves 1/2 the conditions. Now I can't pass another binding into the converter, nor can I create a property that handles both (needs to be XAML only solution).

 <AdornedElementPlaceholder
                            x:Name="errorAdorner" />
                    ...

  <Grid
     x:Name="ErrorDetails"
     Visibility="{Binding ElementName=errorAdorner, Path=AdornedElement.IsFocused, Converter={StaticResource BooleanToVisibilityConverter}}" />

                   ...

What I want to do is use triggers to handle this, the only problem is I can't access the AdornedElement's properties on a trigger.

Something like this ...

        <Trigger
            SourceName="errorAdorner"
            Property="AdornedElement.IsFocused"
            Value="True">
            <Setter
                TargetName="ErrorDetails"
                Property="Visibility"
                Value="Visible" />
        </Trigger>

This would also help as part of what I want to do is trigger animations, rather than just setting the visibility.

Any help would be great.


Solution

  • What you're looking for is called a MultiBinding and is built into WPF (though not in Silverlight.)

    <Grid>
        <Grid.Resources>
            <c:BooleanPairToVisibilityConverter x:Key="booleanPairToVisibility" />
        </Grid.Resources>
        <Grid.Visibility>
            <MultiBinding Converter="{StaticResource booleanPairToVisibility}">
                <Binding ElementName="errorAdorner" Path="AdornedElement.IsFocused" />
                <Binding ElementName="errorAdorner" Path="AdornedElement.IsMouseOver" />
            </MultiBinding>
        </Grid.Visibility>
    </Grid>
    

    Then you need a simple IMultiValueConverter to translate those values into a Visibility:

    public class BooleanPairToVisibilityConverter : IMultiValueConverter {
        public object Convert( object[] values, Type targetType, object parameter, CultureInfo culture )
        {
            if ( 2 != values.Length ) throw new ArgumentException( "values" );
            return ( (bool)values[0] || (bool)values[1] ) ? Visibility.Visible : Visibility.Collapsed;
        }
    
        public object[] ConvertBack( object value, Type[] targetTypes, object parameter, CultureInfo culture )
        { throw new NotSupportedException(); }
    }
    

    Admittedly, this doesn't solve the second question about how to do this with Triggers. I wouldn't...

    If you want an animation around a change in the ErrorDetails element's visibility, set the trigger on the visibility property directly - it should get invoked when the MultiBinding causes the DependencyProperty's value to change. Also, it might be worth considering Behaviors to accomplish that instead as they're a little more straightforward for attaching simple animations.