Search code examples
c#.netwpfdata-bindingwpf-animation

Problem with Enter&Exit Actions animation in DataTrigger and MultiDataTrigger in C# WPF


I want to achieve the following: If ShowGrid = false, then Border.Padding = "0 0 0 0", otherwise Border.Padding = "0 0 0 40" If ShowGrid = true and IsMouseOver = true, then Border.Padding = "0 0 0 0" If ShowGrid = true and IsMouseOver = false, then Border.Padding = "0 0 0 40"

Animation Description: If the grid is not displayed, we show the contents of the Border by gradually reducing Padding to 0 over 0.25 seconds. As a result, we can always see the contents of the Border while the grid is not displayed. However, if the grid appears, we need to increase the Padding to "0 0 0 40" over 0.25 seconds, which means that the grid is displayed and the contents of the Border are not visible. If the grid is displayed, when hovering over its area with the mouse, we should see its contents by reducing Padding to 0, and when IsMouseOver = false, we hide it again by increasing Padding to "0 0 0 40".

Problem description: If the code is used without ThicknessAnimation, everything works perfectly - if ShowGrid = false, the content of the Border is always displayed and does not respond to mouse movements, and if ShowGrid = true, the content of the Border is displayed only when the mouse is hovering over the Border. If the code is used with ThicknessAnimation, the behavior changes depending on the order of the triggers. The first behavior, where the DataTrigger order is first: if ShowGrid = false, the content of the Border is not displayed, but if ShowGrid = true, the content of the Border is displayed only when the mouse is hovering over the Border. That is, the MultiDataTrigger works. The second behavior, where the MultiDataTrigger order is first: if ShowGrid = true, the content of the Border is not displayed when hovering over the Border, but if ShowGrid = false, the content of the Border is displayed. That is, the DataTrigger works.

Here is a fully functional reference without animation:

<Border.Style>
                <Style TargetType="{x:Type Border}">
                    <Setter Property="Padding" Value="0 0 0 40" />
                    <Style.Triggers>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}"
                                           Value="True" />
                                <Condition Binding="{Binding ShowGrid}" Value="True" />
                            </MultiDataTrigger.Conditions>
                            <MultiDataTrigger.Setters>
                                <Setter Property="Padding" Value="0" />
                            </MultiDataTrigger.Setters>
                        </MultiDataTrigger>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}"
                                           Value="False" />
                                <Condition Binding="{Binding ShowGrid}" Value="True" />
                            </MultiDataTrigger.Conditions>
                            <MultiDataTrigger.Setters>
                                <Setter Property="Padding" Value="0 0 0 40" />
                            </MultiDataTrigger.Setters>
                        </MultiDataTrigger>

                        <DataTrigger Binding="{Binding ShowGrid}" Value="False">
                            <Setter Property="Padding" Value="0" />
                        </DataTrigger>

                    </Style.Triggers>
                </Style>
            </Border.Style>

And here's what I'm trying to do, and as far as I understand, there is a problem with Enter & Exit Actions:

<Border.Style>
                <Style TargetType="{x:Type Border}">
                    <Setter Property="Padding" Value="0 0 0 40" />
                    <Style.Triggers>
                        
                        <DataTrigger Binding="{Binding ShowGrid}" Value="False">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 0"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 40"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}"
                                           Value="True" />
                                <Condition Binding="{Binding ShowGrid}" Value="True" />
                            </MultiDataTrigger.Conditions>
                            <MultiDataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </MultiDataTrigger.EnterActions>
                            <MultiDataTrigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 40"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </MultiDataTrigger.ExitActions>
                        </MultiDataTrigger>

                       

                    </Style.Triggers>
                </Style>
            </Border.Style>

Demonstration of behavior without animation Demonstration of behavior with animation

I would appreciate your help!

MainWindow.xaml

    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="35" />
            <RowDefinition Height="20" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Content="{Binding ButtonMessage, Mode=OneWay}" Command="{Binding ChangeShowGrid}" />
        <Border Grid.Row="1" HorizontalAlignment="Stretch" Background="Transparent" Padding="0 0 0 40" >
            <ItemsControl>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.Items>
                    <TextBlock Text="Hello, world!" Foreground="Red" />
                    <TextBlock Text="Good bye, world!" Foreground="Red" />
                </ItemsControl.Items>
            </ItemsControl>
            <Border.Style>
                <Style TargetType="{x:Type Border}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ShowGrid}" Value="False">
                            <DataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 0" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.EnterActions>
                            <DataTrigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 40" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </DataTrigger.ExitActions>
                        </DataTrigger>

                        <MultiDataTrigger>
                            <MultiDataTrigger.Conditions>
                                <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True" />
                                <Condition Binding="{Binding ShowGrid}" Value="True" />
                            </MultiDataTrigger.Conditions>
                            <MultiDataTrigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </MultiDataTrigger.EnterActions>
                            <MultiDataTrigger.ExitActions>
                                <BeginStoryboard>
                                    <Storyboard>
                                        <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 40" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </MultiDataTrigger.ExitActions>
                        </MultiDataTrigger>
                    </Style.Triggers>
                </Style>
            </Border.Style>
        </Border>
    </Grid>

MainWindowViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

public class MainWindowViewModel : ObservableObject
{
    private bool _showGrid;

    public MainWindowViewModel()
    {
        ChangeShowGrid = new RelayCommand(() => ShowGrid = !ShowGrid);
    }

    public string ButtonMessage => ShowGrid ? "Hide Grid" : "Show Grid";

    public bool ShowGrid
    {
        get => _showGrid;
        set
        {
            if (_showGrid == value) return;
            _showGrid = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(ButtonMessage));
        }
    }


    public RelayCommand ChangeShowGrid { get; }
}

Solution

  • I am a little confused by the naming of properties and messages but I think you can fix the animations by setting FillBehavior.

    <Style TargetType="{x:Type Border}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding ShowGrid}" Value="False">
                <DataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 0"
                                                FillBehavior="HoldEnd"/>
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
                <DataTrigger.ExitActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 40"
                                                FillBehavior="Stop"/>
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.ExitActions>
            </DataTrigger>
    
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding ShowGrid}" Value="True"/>
                    <Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="True"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.EnterActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0"
                                                FillBehavior="HoldEnd"/>
                        </Storyboard>
                    </BeginStoryboard>
                </MultiDataTrigger.EnterActions>
                <MultiDataTrigger.ExitActions>
                    <BeginStoryboard>
                        <Storyboard>
                            <ThicknessAnimation Duration="0:0:0.25" Storyboard.TargetProperty="Padding" To="0 0 0 40"
                                                FillBehavior="Stop"/>
                        </Storyboard>
                    </BeginStoryboard>
                </MultiDataTrigger.ExitActions>
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>