C#WPF当滥用 "Toggle "时,处理活动的Storyboard动画。

问题描述 投票:0回答:1

基本上,我有这样一个动画,当你点击按钮时,它会切换网格,显示或隐藏第二列(包含Button2)......。我的问题是,当你点击按钮时,动画会被排队,所以它需要在做第二个动画之前完成第一个动画。我唯一的解决方法是在动画激活时禁用按钮,然后在动画完成后重新启用它们。但我正试图找到一种方法,使活动中的动画以某种方式被中断,而是使用网格的当前宽度来启动第二个动画。

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    _toggle = false;
    GridBodyWidthOne = new GridLength(1, GridUnitType.Star);
    GridBodyWidthZero = new GridLength(0, GridUnitType.Star);
    GridBody0Width = GridBodyWidthOne;
    GridBody1Width = GridBodyWidthZero;

    storyboard = this.FindResource("expandBody0") as Storyboard;
    storyboard.FillBehavior = FillBehavior.Stop;
    storyboard.Completed += (object o, EventArgs ea) => {
        GridBody0.Width = GridBodyWidthOne;
        GridBody1.Width = GridBodyWidthZero;
        GridBody0.BeginAnimation(ColumnDefinition.WidthProperty, null);
        GridBody1.BeginAnimation(ColumnDefinition.WidthProperty, null);
        GridSplitter0.IsEnabled = false;};

    storyboard = this.FindResource("retractBody0") as Storyboard;
    storyboard.FillBehavior = FillBehavior.Stop;
    storyboard.Completed += (object o, EventArgs ea) => {
        GridBody0.Width = GridBodyWidthOne;
        GridBody1.Width = GridBodyWidthOne;
        GridBody0.BeginAnimation(ColumnDefinition.WidthProperty, null);
        GridBody1.BeginAnimation(ColumnDefinition.WidthProperty, null);
        GridSplitter0.IsEnabled = true;};
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    ToggleMainBody1();
}

private void ToggleMainBody1()
{
    double maxWidth = GridBody0.ActualWidth + GridBody1.ActualWidth;
    double width0 = GridBody0.ActualWidth / maxWidth;
    double width1 = GridBody1.ActualWidth / maxWidth;
    GridBody0Width = new GridLength(width0, GridUnitType.Star);
    GridBody1Width = new GridLength(width1, GridUnitType.Star);

    if (!_toggle)
        RevealMainBody1();
    else
        HideMainBody1();

    _toggle = !_toggle;
}

private void HideMainBody1()
{
    //storyboard = this.FindResource("retractBody0") as Storyboard;
    //storyboard.Stop(this);
    storyboard = this.FindResource("expandBody0") as Storyboard;
    storyboard.Begin(this);
}

private void RevealMainBody1()
{
    //storyboard = this.FindResource("expandBody0") as Storyboard;
    //storyboard.Stop(this);
    storyboard = this.FindResource("retractBody0") as Storyboard;
    storyboard.Begin(this);
}

至于为什么我把FillBehavior设置为'Stop',如果GridSplitter设置为'HoldEnd',我就会有问题。'HoldEnd'实际上很好用,但当我通过GridSplitter调整它时就不行了。所以,基本上,当你双击toggle按钮时,问题就会发生(或者在第一次动画没有完成的情况下,两次点击toggle)。

<Window x:Class="ColumnAdjustmentV2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ColumnAdjustmentV2"
    xmlns:g="clr-namespace:ColumnAdjustmentV2"
    mc:Ignorable="d"
    Title="MainWindow" Height="650" Width="800" Loaded="Window_Loaded" SizeChanged="Window_SizeChanged" StateChanged="Window_StateChanged" >
<Window.Resources>
    <Storyboard x:Key="expandBody0">
        <!--<g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody0" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody0Width}" To="{Binding Path=GridBodyWidthOne}" />-->
        <g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody1" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody1Width}" To="{Binding Path=GridBodyWidthZero}" />
    </Storyboard>
    <Storyboard x:Key="retractBody0">
        <!--<g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody0" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody0Width}" To="{Binding Path=GridBodyWidthOne}" />-->
        <g:GridLengthAnimation BeginTime="0:0:0" Duration="0:0:0.5" Storyboard.TargetName="GridBody1" Storyboard.TargetProperty="Width" From="{Binding Path=GridBody1Width}" To="{Binding Path=GridBodyWidthOne}" />
    </Storyboard>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="500" />
        <RowDefinition Height="100" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid>
        <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="GridBody0" Width="{Binding Path=GridBody0Width, Mode=TwoWay}" />
        <ColumnDefinition x:Name="GridBodyDivider" Width="Auto" />
        <ColumnDefinition x:Name="GridBody1" Width="{Binding Path=GridBody1Width, Mode=TwoWay}" />
        </Grid.ColumnDefinitions>

        <Button x:Name="Button1" Grid.Column="0" Content="Button1" Click="Button_Click" />
        <GridSplitter x:Name="GridSplitter0" Grid.Column="1" Background="#FFCC0099" HorizontalAlignment="Center" Margin="0" Width="4" VerticalAlignment="Stretch" LayoutUpdated="GridSplitter_LayoutUpdated" MouseDoubleClick="GridSplitter_MouseDoubleClick" PreviewKeyDown="GridSplitter_PreviewKeyDown" PreviewMouseUp="GridSplitter_PreviewMouseUp" />
        <Button x:Name="Button2" Grid.Column="2" Content="Button2" Click="Button_Click" />
    </Grid>

    <Grid Grid.Row="1">
        <Button x:Name="Button3" Content=""/>
    </Grid>
</Grid>

xaml

https:/www.youtube.comwatch?v=2iE8cZC6EFQ

c# wpf animation storyboard
1个回答
1
投票

我已经准备了一个示例项目,我认为可能对你有帮助。https:/github.comDrreamerAnimationReverse。在这个例子中,我使用动画将一个按钮从最小宽度调整到最大宽度。要启动动画或以相反的方向运行动画,只需点击按钮即可。

<Grid x:Name="rootGrid">
  <Button x:Name="button" MinWidth="40" Width="40" Content="Expand" 
          local:MainWindow.AnimationDuration="0:0:2"  HorizontalAlignment="Left" Click="button_Click" >

     <Button.Triggers>
        <EventTrigger RoutedEvent="Button.Click">
           <BeginStoryboard>
              <Storyboard>
                 <DoubleAnimation
                            Storyboard.TargetName="button"
                            Storyboard.TargetProperty="Width"
                            From="{Binding ActualWidth,  ElementName=button}" 
                            Duration="{Binding Path=(local:MainWindow.AnimationDuration), ElementName=button}">
                    <DoubleAnimation.To>
                       <MultiBinding Converter="{StaticResource ToWidthConverter}">
                          <Binding  ElementName="button" Path="Tag" />
                          <Binding  ElementName="button" Path="MinWidth" />
                          <Binding  ElementName="rootGrid" Path="ActualWidth" />
                       </MultiBinding>
                    </DoubleAnimation.To>
                 </DoubleAnimation>     
              </Storyboard>
           </BeginStoryboard>
        </EventTrigger>
     </Button.Triggers>
  </Button>
</Grid>

这里我使用绑定来动态设置To和Duration属性。我在Tag属性中存储了当前动画方向的信息。持续时间是根据当前元素的宽度从Button.Click事件中动态计算出来的。这里的目标是确保动画总是以相同的速度进行。

    private void button_Click(object sender, RoutedEventArgs e) {
   double currentPosition = (button.ActualWidth - button.MinWidth) / (rootGrid.ActualWidth - button.MinWidth);
   if (button.Tag is bool && (bool)button.Tag)
      button.Tag = false;
   else {
      currentPosition = 1 - currentPosition;
      button.Tag = true;
   }
   SetAnimationDuration(button, new Duration(TimeSpan.FromSeconds(5 * currentPosition)));
}   
© www.soinside.com 2019 - 2024. All rights reserved.