首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >VisualStateManager不像通告的那样工作

VisualStateManager不像通告的那样工作
EN

Stack Overflow用户
提问于 2011-02-15 18:29:08
回答 4查看 5K关注 0票数 18

下面这个问题已经困扰我好几天了,但我只能把它提炼成最简单的形式。考虑以下XAML:

代码语言:javascript
复制
<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控件的模板,使其背景依赖于其状态:

选中的红色

  • = Green

  • Unchecked =红色

  • 检查(过渡)=浅色Green

  • Unchecking (过渡)=浅红色

因此,当您选中其中一个复选框时,您会期望它在短时间内变为浅绿色,然后变为绿色。类似地,当取消选中时,您会期望它在短时间内变为浅红色,然后变为红色。

它通常就是这么做的。,但并不总是。

使用该程序足够长的时间(我可以在大约30秒内得到它),你会发现在视觉状态下,过渡动画有时会胜过它。也就是说,复选框在选中时将继续显示为浅绿色,而在取消选中时将继续显示为浅红色。这是一个屏幕截图,说明了我的意思,是在配置转换所需的3秒之后拍摄的:

当发生这种情况时,并不是因为控件没有成功转换到目标状态。它声称处于正确的状态。我通过在调试器中检查以下内容来验证这一点(对于上面截图中记录的特定情况):

代码语言:javascript
复制
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;

如果我为动画启用跟踪,当转换成功完成时,我会得到以下输出:

代码语言:javascript
复制
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 : 

当转换未能成功完成时,我会得到以下输出:

代码语言:javascript
复制
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吗?对于此问题,是否有任何已知的解释或解决方法?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-02-18 03:25:14

我已经能够识别并修复问题,如下所示:

首先,我将我的repro项目降级到WPF 3.5,并从CodePlex获取了.NET工具包的源代码。我将WPF Toolkit项目添加到我的解决方案中,并从Repro项目中添加了对它的引用。

接下来,我运行了这个应用程序,并确保我仍然可以重现这个问题。果然,这样做很容易。

然后我打开了VisualStateManager.cs文件,并开始在关键位置添加一些诊断程序,这些诊断程序会告诉我哪些代码正在运行,哪些代码没有运行。通过添加这些诊断,并比较好的转换和坏的转换的输出,我很快就能够识别出当问题显现时以下代码没有运行:

代码语言:javascript
复制
// 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,而没有停止早期的故事板,这给我带来了一些问题。不知道你的情况是否相似,但我想分享一下这个花边新闻。

小路

有了这个洞察力,我在中更改了这一行

代码语言:javascript
复制
group.StartNewThenStopOld(element, transition.Storyboard, dynamicTransition); 

要这样做:

代码语言:javascript
复制
var masterStoryboard = new Storyboard();

if (transition.Storyboard != null)
{
    masterStoryboard.Children.Add(transition.Storyboard);
}

masterStoryboard.Children.Add(dynamicTransition);
group.StartNewThenStopOld(element, masterStoryboard);

还有--瞧--我以前间歇性失败的复制品现在每次都能工作了!

因此,这确实解决了WPF动画子系统中的bug或奇怪行为。

票数 16
EN

Stack Overflow用户

发布于 2011-02-15 22:45:39

似乎在选中和未选中的故事板上设置Duration="0"是罪魁祸首。删除它可以解决问题。我不确定我理解为什么,除非故事板以某种方式链接到相应的转换。

无论如何,我想我为你找到了一个更干净的解决方案。如果你把你的ControlTemplate改成这样,那么它在没有转换的情况下也能完成同样的事情。

代码语言:javascript
复制
<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>
票数 2
EN

Stack Overflow用户

发布于 2011-09-23 03:46:54

不知道这是否与你的问题有关,但我也偶然发现了AnimationClock.Completed在用一个正在运行的动画替换另一个动画时不能可靠地触发的问题。我认为这是一个垃圾收集和引用/根的问题。当AnimationClock仍在运行但不再以某种方式被引用时,它可能会在任何时间点被垃圾回收。如果在垃圾回收发生之前到达end,则触发Completed,否则不触发。这导致了一种非常不可预测的行为。

我的解决方法是最初将我的时钟添加到某个收集中(强制将其作为根,从而防止垃圾收集),并在完成时将其从收集中删除,然后完成时100%都会触发,并且没有内存泄漏。

这只是我的两个观点。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5002501

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档