下面这个问题已经困扰我好几天了,但我只能把它提炼成最简单的形式。考虑以下XAML:
<Window x:Class="VSMTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="CheckBox">
<Setter Property="Margin" Value="3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CheckBox">
<Grid x:Name="Root">
<Grid.Background>
<SolidColorBrush x:Name="brush" Color="White"/>
</Grid.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CheckStates">
<VisualStateGroup.Transitions>
<VisualTransition To="Checked" GeneratedDuration="00:00:03">
<Storyboard Name="CheckingStoryboard">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="LightGreen"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>
<VisualTransition To="Unchecked" GeneratedDuration="00:00:03">
<Storyboard Name="UncheckingStoryboard">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="LightSalmon"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState Name="Checked">
<Storyboard Name="CheckedStoryboard" Duration="0">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Green"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState Name="Unchecked">
<Storyboard Name="UncheckedStoryboard" Duration="0">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="Red"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<CheckBox x:Name="cb1">Check Box 1</CheckBox>
<CheckBox x:Name="cb2">Check Box 2</CheckBox>
<CheckBox x:Name="cb3">Check Box 3</CheckBox>
</StackPanel>
</Window>
它只需重新设置CheckBox
控件的模板,使其背景依赖于其状态:
选中的红色
因此,当您选中其中一个复选框时,您会期望它在短时间内变为浅绿色,然后变为绿色。类似地,当取消选中时,您会期望它在短时间内变为浅红色,然后变为红色。
它通常就是这么做的。,但并不总是。
使用该程序足够长的时间(我可以在大约30秒内得到它),你会发现在视觉状态下,过渡动画有时会胜过它。也就是说,复选框在选中时将继续显示为浅绿色,而在取消选中时将继续显示为浅红色。这是一个屏幕截图,说明了我的意思,是在配置转换所需的3秒之后拍摄的:
当发生这种情况时,并不是因为控件没有成功转换到目标状态。它声称处于正确的状态。我通过在调试器中检查以下内容来验证这一点(对于上面截图中记录的特定情况):
var vsgs = VisualStateManager.GetVisualStateGroups(VisualTreeHelper.GetChild(this.cb2, 0) as FrameworkElement);
var vsg = vsgs[0];
// this is correctly reported as "Unselected"
var currentState = vsg.CurrentState.Name;
如果我为动画启用跟踪,当转换成功完成时,我会得到以下输出:
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='44177654'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='6148812'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='8261103'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='36205315'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='18626439'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 3 : Storyboard has been removed; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='44177654'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'
System.Windows.Media.Animation Stop: 3 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='36893403'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='CheckingStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='49590434'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='<null>'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 3 : Storyboard has been removed; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='36893403'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='CheckingStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'
System.Windows.Media.Animation Stop: 3 :
System.Windows.Media.Animation Start: 3 : Storyboard has been removed; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='49590434'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='<null>'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'
System.Windows.Media.Animation Stop: 3 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='16977025'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='CheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 3 : Storyboard has been removed; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='16977025'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='CheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'
System.Windows.Media.Animation Stop: 3 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='16977025'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='CheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
当转换未能成功完成时,我会得到以下输出:
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='44177654'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='6148812'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='8261103'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='36205315'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='18626439'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 3 : Storyboard has been removed; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='44177654'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='UncheckedStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'
System.Windows.Media.Animation Stop: 3 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='36893403'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='CheckingStoryboard'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
System.Windows.Media.Animation Start: 1 : Storyboard has begun; Storyboard='System.Windows.Media.Animation.Storyboard'; Storyboard.HashCode='49590434'; Storyboard.Type='System.Windows.Media.Animation.Storyboard'; StoryboardName='<null>'; TargetElement='System.Windows.Controls.Grid'; TargetElement.HashCode='41837403'; TargetElement.Type='System.Windows.Controls.Grid'; NameScope='<null>'
System.Windows.Media.Animation Stop: 1 :
前12行与转换成功时完全相同,但最后10行完全丢失!
我已经通读了我能找到的所有VSM文档,但无法对这种反复无常的行为做出解释。
我可以假设这是VSM中的一个bug吗?对于此问题,是否有任何已知的解释或解决方法?
发布于 2011-02-18 03:25:14
我已经能够识别并修复问题,如下所示:
首先,我将我的repro项目降级到WPF 3.5,并从CodePlex获取了.NET工具包的源代码。我将WPF Toolkit项目添加到我的解决方案中,并从Repro项目中添加了对它的引用。
接下来,我运行了这个应用程序,并确保我仍然可以重现这个问题。果然,这样做很容易。
然后我打开了VisualStateManager.cs文件,并开始在关键位置添加一些诊断程序,这些诊断程序会告诉我哪些代码正在运行,哪些代码没有运行。通过添加这些诊断,并比较好的转换和坏的转换的输出,我很快就能够识别出当问题显现时以下代码没有运行:
// Hook up generated Storyboard's Completed event handler
dynamicTransition.Completed += delegate
{
if (transition.Storyboard == null ||
transition.ExplicitStoryboardCompleted)
{
if (ShouldRunStateStoryboard(control, element, state, group))
{
group.StartNewThenStopOld(element, state.Storyboard);
}
group.RaiseCurrentStateChanged(element, lastState, state,
control);
}
transition.DynamicStoryboardCompleted = true;
};
因此,bug的性质从VSM中的问题转变为Storyboard.Completed
事件中的问题,但并不总是被引发。这是一个我以前遇到过的问题,似乎是任何WPF开发人员在动画方面做任何事情时都会感到焦虑的一个来源,即使是稍微不寻常的。
在整个过程中,我一直在WPF Disciples google group上发布我的发现,正是在这一点上,Pavan Podila用这个宝石做出了回应:
Kent
我过去遇到过故事板不能触发已完成的事件的问题。我意识到的是,如果您直接替换Storyboard,而不是首先停止它,您可能会看到一些无序的已完成事件。在我的例子中,我将新的故事板应用于相同的FrameworkElement,而没有停止早期的故事板,这给我带来了一些问题。不知道你的情况是否相似,但我想分享一下这个花边新闻。
小路
有了这个洞察力,我在中更改了这一行
group.StartNewThenStopOld(element, transition.Storyboard, dynamicTransition);
要这样做:
var masterStoryboard = new Storyboard();
if (transition.Storyboard != null)
{
masterStoryboard.Children.Add(transition.Storyboard);
}
masterStoryboard.Children.Add(dynamicTransition);
group.StartNewThenStopOld(element, masterStoryboard);
还有--瞧--我以前间歇性失败的复制品现在每次都能工作了!
因此,这确实解决了WPF动画子系统中的bug或奇怪行为。
发布于 2011-02-15 22:45:39
似乎在选中和未选中的故事板上设置Duration="0"
是罪魁祸首。删除它可以解决问题。我不确定我理解为什么,除非故事板以某种方式链接到相应的转换。
无论如何,我想我为你找到了一个更干净的解决方案。如果你把你的ControlTemplate改成这样,那么它在没有转换的情况下也能完成同样的事情。
<ControlTemplate TargetType="CheckBox">
<Grid x:Name="Root">
<Grid.Background>
<SolidColorBrush x:Name="brush" Color="White"/>
</Grid.Background>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CheckStates">
<VisualState Name="Checked">
<Storyboard x:Name="CheckedStoryboard">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="LightGreen"/>
<DiscreteColorKeyFrame KeyTime="00:00:03" Value="Green"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState Name="Unchecked">
<Storyboard x:Name="UncheckedStoryboard">
<ColorAnimationUsingKeyFrames Storyboard.TargetName="brush" Storyboard.TargetProperty="Color">
<DiscreteColorKeyFrame KeyTime="0" Value="LightSalmon"/>
<DiscreteColorKeyFrame KeyTime="00:00:03" Value="Red"/>
</ColorAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter/>
</Grid>
</ControlTemplate>
发布于 2011-09-23 03:46:54
不知道这是否与你的问题有关,但我也偶然发现了AnimationClock.Completed在用一个正在运行的动画替换另一个动画时不能可靠地触发的问题。我认为这是一个垃圾收集和引用/根的问题。当AnimationClock仍在运行但不再以某种方式被引用时,它可能会在任何时间点被垃圾回收。如果在垃圾回收发生之前到达end,则触发Completed,否则不触发。这导致了一种非常不可预测的行为。
我的解决方法是最初将我的时钟添加到某个收集中(强制将其作为根,从而防止垃圾收集),并在完成时将其从收集中删除,然后完成时100%都会触发,并且没有内存泄漏。
这只是我的两个观点。
https://stackoverflow.com/questions/5002501
复制相似问题