我正在学习WPF动画,我创建了一个简单的演示应用程序,其中包含非常简单的动画。我将主网格划分为三行; a按钮在顶部排,两个内容行用于屏幕的其余部分,一个红色和一个蓝色。完整的XAML如下。
有两个按钮,Show Red和Show Blue。当按下每个按钮时,我希望按钮行下面的区域通过缓慢的从上到下的擦除来改变颜色。故事板将两行的高度设置为0,然后将所需的行设置为高度1 *,如下所示:
<Storyboard>
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
</Storyboard>
颜色按预期变化,但没有动画。所以,我的问题很简单:为什么动画不起作用?
我正在使用自定义动画类GridLengthAnimation
(改编自this CodeProject article)来为网格长度设置动画。我已经复制了下面的课程。
要重新创建演示项目:要重新创建我的演示项目,请创建一个新的WPF项目(我使用VS 2010)并将MainWindow.xaml
中的XAML替换为以下内容:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Utility="clr-namespace:Utility" Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="Buttons" Height="35" />
<RowDefinition Name="RedRow" Height="0.5*" />
<RowDefinition Name="BlueRow" Height="0.5*" />
</Grid.RowDefinitions>
<!-- Buttons -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Show Red" Width="100" Margin="5" >
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
<Button Content="Show Blue" Width="100" Margin="5" >
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<Utility:GridLengthAnimation Storyboard.TargetName="RedRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" To="0" Duration="0:0:0" />
<Utility:GridLengthAnimation Storyboard.TargetName="BlueRow" Storyboard.TargetProperty="Height" From="0" To="1*" Duration="0:0:5" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Button.Triggers>
</Button>
</StackPanel>
<!-- Grid Fills-->
<Border Grid.Row="1" Background="Red" />
<Border Grid.Row="2" Background="Blue" />
</Grid>
</Window>
MainWindow.xaml
没有添加任何代码隐藏功能。
将C#类添加到名为GridLengthAnimation.cs
的项目中。用以下内容替换该类中的代码:
using System;
using System.Windows.Media.Animation;
using System.Windows;
namespace Utility
{
/// <summary>
/// Enables animation of WPF Grid row heights and column widths.
/// </summary>
/// <remarks>Adapted from Graus & Sivakumar, "WPF Tutorial - Part 2 : Writing a custom animation class",
/// http://www.codeproject.com/KB/WPF/GridLengthAnimation.aspx, retrieved 08/12/2010.</remarks>
internal class GridLengthAnimation : AnimationTimeline
{
static GridLengthAnimation()
{
FromProperty = DependencyProperty.Register("From", typeof(GridLength),
typeof(GridLengthAnimation));
ToProperty = DependencyProperty.Register("To", typeof(GridLength),
typeof(GridLengthAnimation));
}
public override Type TargetPropertyType
{
get
{
return typeof(GridLength);
}
}
protected override Freezable CreateInstanceCore()
{
return new GridLengthAnimation();
}
public static readonly DependencyProperty FromProperty;
public GridLength From
{
get
{
return (GridLength)GetValue(FromProperty);
}
set
{
SetValue(FromProperty, value);
}
}
public static readonly DependencyProperty ToProperty;
public GridLength To
{
get
{
return (GridLength)GetValue(ToProperty);
}
set
{
SetValue(ToProperty, value);
}
}
public override object GetCurrentValue(object defaultOriginValue, object defaultDestinationValue, AnimationClock animationClock)
{
double fromVal = ((GridLength)GetValue(FromProperty)).Value;
double toVal = ((GridLength)GetValue(ToProperty)).Value;
if (animationClock.CurrentProgress != null)
{
if (fromVal > toVal)
{
return new GridLength((1 - animationClock.CurrentProgress.Value) * (fromVal - toVal) + toVal, GridUnitType.Star);
}
else
{
return new GridLength(animationClock.CurrentProgress.Value * (toVal - fromVal) + fromVal, GridUnitType.Star);
}
}
else
{
return null;
}
}
}
}
答案 0 :(得分:0)
我在this blog post找到了答案。事实证明,设置高度或宽度属性存在问题。我通过使用溶解效果而不是擦除来解决问题。要为dissolve设置动画,请在同一Grid行和列中声明两个控件,这些控件将在彼此之上加载它们。最后声明默认控件,这将使其成为可见控件。然后,将默认控件的“不透明度”值设置为零以将其隐藏,并将其设置为1以显示它。
如果要设置动画的控件是UserControls或其他需要单击的控件,则需要再执行一步。这是因为将控件的不透明度设置为零只会使其不可见。它仍然会阻止点击它下面的控件。因此,在默认控件上声明一个Render.Transform,然后设置ScaleY属性的动画,使其在不可见时设置为0,在显示时设置为1.
以下是我正在制作的制作应用中的一个示例。它在资源管理器样式界面的“导航器”窗格中在注释列表和日历(两个不同的UserControl)之间切换。以下是两个控件的声明:
<!-- ClientArea: Navigator -->
<Grid x:Name="Navigator">
<View:CalendarNavigator x:Name="Calendar" />
<View:NoteListNavigator x:Name="NoteList">
<View:NoteListNavigator.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1" />
</View:NoteListNavigator.RenderTransform>
</View:NoteListNavigator>
</Grid>
请注意注释列表中ScaleTransform
的声明。我使用一些功能区按钮在两个UserControls之间切换:
<ribbon:RibbonToggleButton x:Name="NoteListViewButton" LargeImageSource="..\Images\ListViewLarge.png" SmallImageSource="..\Images\ListViewSmall.png" Label="Note List" Click="OnViewButtonClick">
<ribbon:RibbonToggleButton.Triggers>
<EventTrigger RoutedEvent="ribbon:RibbonToggleButton.Checked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="(View:NoteListNavigator.RenderTransform).(ScaleTransform.ScaleY)" To="1" Duration="0:0:0" />
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
<ribbon:RibbonToggleButton x:Name="CalendarViewButton" LargeImageSource="..\Images\CalendarViewLarge.png" SmallImageSource="..\Images\CalendarViewSmall.png" Label="Calendar" Click="OnViewButtonClick">
<ribbon:RibbonToggleButton.Triggers>
<EventTrigger RoutedEvent="ribbon:RibbonToggleButton.Checked">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="Opacity" To="0" Duration="0:0:1" />
<DoubleAnimation Storyboard.TargetName="NoteList" Storyboard.TargetProperty="(View:NoteListNavigator.RenderTransform).(ScaleTransform.ScaleY)" To="0" Duration="0:0:0" />
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</ribbon:RibbonToggleButton.Triggers>
</ribbon:RibbonToggleButton>
当日历显示时,ScaleY转换会使隐藏的音符列表不受影响,这样我就可以单击我的日历控件。请注意,我需要对Storyboard中的ScaleY属性进行完全限定的引用。这就是引用括在括号中的原因。
希望能帮助别人前进!这很可能是我,因为我可能会忘记我是怎么做到的......