向“切换”发送垃圾邮件时,C#/WPF 处理活动的故事板动画
Posted
技术标签:
【中文标题】向“切换”发送垃圾邮件时,C#/WPF 处理活动的故事板动画【英文标题】:C#/WPF Handling active Storyboard Animations when spamming the 'Toggle' 【发布时间】:2020-08-25 19:27:44 【问题描述】:基本上我有这个动画,当您单击按钮时,它会切换网格以显示或隐藏第二列(包含 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 设置为“停止”,如果 GridSplitter 设置为“HoldEnd”,我会遇到问题。 “HoldEnd”实际上效果很好,但当我通过 GridSplitter 调整它时就不行了。所以基本上,当您双击切换按钮(或触发切换两次而没有完成第一个动画)时,就会出现问题。
<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>
https://www.youtube.com/watch?v=2iE8cZC6EFQ
【问题讨论】:
您能否将完整的 XAML 包含为文本(而不是屏幕截图)? 使用 XAML 更新,如果需要,我还可以更新后面的代码以包含其他所有内容(我只是尝试将代码缩减为我认为重要的部分,但我可能会遗漏一些东西)...谢谢! 【参考方案1】:我准备了一个我认为可能对你有帮助的示例项目:https://github.com/Drreamer/AnimationReverse 在此示例中,我使用动画将按钮的大小从最小宽度调整为最大宽度。要开始动画或反向运行动画,只需单击按钮。
<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 属性中。 Duration 是根据当前元素宽度从 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)));
【讨论】:
显然我的数学不正确(忘记交换'to'和'from'),但看到你的答案,我想研究它并将其添加到我的武器库中,谢谢!以上是关于向“切换”发送垃圾邮件时,C#/WPF 处理活动的故事板动画的主要内容,如果未能解决你的问题,请参考以下文章
使用谷歌日历的谷歌数据 API 创建新活动时向客人发送电子邮件