Search code examples
wpfstoryboarddatatrigger

Controlling DataTrigger ExitActions


I have a CheckBox (placed above an image) whose Opacity is set to 0 by default. When IsMouseOver is true, the CheckBox Opacity property is animated from 0 to 1 and reversed when IsMouseOver is false. However, if the CheckBox IsSelected is true, then the reverse animation(i.e DataTrigger.ExitActions) should not execute. i.e CheckBox should remain visible if it is checked. In my XAML the checkbox is disappearing even when it is checked.

Following is my XAML:

<CheckBox VerticalAlignment="Center" HorizontalAlignment="Center">
        <CheckBox.Style>
            <Style TargetType="{x:Type CheckBox}">
                <Setter Property="Opacity" Value="0" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ElementName=imageGrid, Path=IsMouseOver}" Value="True">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard Timeline.DesiredFrameRate="100">
                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" To="1" Duration="00:00:00.400" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>
                        <DataTrigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard Timeline.DesiredFrameRate="100">
                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="00:00:00.400" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.ExitActions>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Mode=Self}}" Value="true">
                        <Setter Property="Opacity" Value="1" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </CheckBox.Style>
    </CheckBox>

Implemented the way @feO2x suggested. However it is not working as expected. Got it work in a simple application. However I intend to use the checkbox in a ListViewItem. Following is my XAML:

<Style x:Key="ThumbView_ItemContainerStyle" TargetType="{x:Type ListViewItem}">
    <Setter Property="Background" Value="Transparent"/>
    <Setter Property="HorizontalContentAlignment" Value="Center"/>
    <Setter Property="VerticalContentAlignment" Value="Center"/>
    <Setter Property="Padding" Value="0"/>
    <Setter Property="Height" Value="175" />
    <Setter Property="Width" Value="125" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListViewItem}">
                <Grid Background="Pink" x:Name="mygrid">
                    <Rectangle Name="LBRect" Fill="Transparent" Opacity="0.195" />
                    <Image Name="posterImg" Margin="0,0,0,0" RenderOptions.BitmapScalingMode="HighQuality" SnapsToDevicePixels="True" HorizontalAlignment="Center" VerticalAlignment="Center" Source="{Binding Path=ProfilePic}" Height="125" MaxWidth="125" />
                    <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" VerticalAlignment="Center" HorizontalAlignment="Center" x:Name="CheckBox">
                        <CheckBox.RenderTransform>
                            <ScaleTransform ScaleX="1.1" ScaleY="1.1" />
                        </CheckBox.RenderTransform>

                        <CheckBox.Style>
                            <Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource ThumbViewCheckBoxStyle}">
                                <Setter Property="Opacity" Value="0" />
                                <Style.Triggers>
                                    <MultiDataTrigger>
                                        <MultiDataTrigger.Conditions>
                                            <Condition Binding="{Binding Path=IsMouseOver, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListViewItem}}}" Value="False" />
                                            <Condition Binding="{Binding ElementName=CheckBox, Path=IsChecked}" Value="False" />
                                        </MultiDataTrigger.Conditions>
                                        <MultiDataTrigger.EnterActions>
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.0" Duration="0:0:0.4" />
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </MultiDataTrigger.EnterActions>
                                        <MultiDataTrigger.ExitActions>
                                            <BeginStoryboard>
                                                <Storyboard>
                                                    <DoubleAnimation Storyboard.TargetProperty="Opacity" To="1.0" Duration="0:0:0.4" />
                                                </Storyboard>
                                            </BeginStoryboard>
                                        </MultiDataTrigger.ExitActions>
                                    </MultiDataTrigger>
                                </Style.Triggers>
                            </Style>
                        </CheckBox.Style>
                    </CheckBox>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Solution

  • This can be achieved with IMultiValueConverter in place. Pass two bindings to it i.e. IsMouseOver and IsChecked.

    Converter:

    public class MyConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, 
                              CultureInfo culture)
        {
            bool isMouseOver = (bool)values[0];
            bool isChecked = (bool)values[1];
    
            return isChecked || isMouseOver;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes,
                                    object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    XAML:

    <DataTrigger Value="True">
       <DataTrigger.Binding>
          <MultiBinding Converter="{StaticResource MyConverter}">
             <Binding Path="IsMouseOver" ElementName="imageGrid"/>
             <Binding Path="IsChecked" RelativeSource="{RelativeSource Self}"/>
          </MultiBinding>
       </DataTrigger.Binding>
       <!-- Rest same trigger -->
    </DataTrigger>
    

    Of course you need to add MyValueConverter instance as resource in your XAML file.