2011-02-15 53 views
18

以下問題一直困擾着我好幾天了,但我只是能夠將其簡化爲最簡單的形式。請看下面的XAML:VisualStateManager未按照所宣傳的那樣工作

<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秒過渡配置後取好:

enter image description here

如果發生這種情況,這是因爲控制未成功轉換到目標狀態。它聲稱處於正確的狀態。我驗證了這個在調試器檢查以下內容(通過上面的截圖中介紹的特定情況下):

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中的一個錯誤?有沒有對這個問題有任何已知的解釋或解決方法?

+1

解決方法之一是退出vsm並使用樣式觸發器/ multitrigger。 – blindmeis 2011-02-15 13:54:52

+1

你已經標記了WPF和Silverlight。兩者都有相同的行爲嗎? – 2011-02-15 14:23:18

+3

@Steve:它的WPF,但看起來他也在尋找任何Silverlight傢伙的幫助,因爲VSM也是Silverlight的一部分。 – decyclone 2011-02-15 14:54:21

回答

16

我已經能夠識別並修復問題如下:

首先,我降級我的攝製項目NET 3.5和CodePlex抓住了WPF工具包的源代碼。我將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; 
}; 

因此,錯誤的性質轉變從VSM在Storyboard.Completed事件問題的問題並不總是被提出。這是我以前所經歷的問題,似乎是很多焦慮的任何WPF開發人員做任何事情,甚至稍微與衆不同的,當涉及到動畫的來源。

在整個過程中,我被張貼在WPF Disciples google group我發現,這是在這一點上Pavan Podila迴應這種寶石:

肯特,

我有問題,在過去的故事板不費一槍他們 完成的事件。我已經意識到 的是,如果您直接替換故事板 ,而沒有先停止它, 您可能會看到一些無序 已完成的事件。在我的情況下,我是 應用較新的故事板到相同的 FrameworkElement,而不停止 較早的故事板,這是 給我一些問題。不知道如果 你的情況是類似的,但想到我會 份額這麼一個小節目。

有了這些見解,我改變了這一行VisualStateManager.cs

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的動畫子系統中的錯誤或古怪的行爲。

2

看起來好像設置Duration="0"在Checked和Unchecked故事板上是罪魁禍首。刪除它可以解決問題。我不確定我是否理解爲什麼,除非故事板以某種方式與相應的轉換鏈接。

但是,我認爲我找到了一個更清潔的解決方案。如果你改變你的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> 
0

不知道,如果這是在所有與您的問題有關,但我也無意中發現了與另一個替換正在運行的動畫時AnimationClock.Completed不可靠的點火問題。我認爲這是垃圾收集和參考/生根的問題。當一個AnimationClock仍在運行但不再以任何方式參考時,它可能會在任何時間點被垃圾收集。如果在垃圾收集發生之前達到結束,則會觸發Completed,否則將觸發Completed。這使得一個非常不可預知的行爲。

我的解決方法是最初將我的時鐘添加到某個集合(強制它被固定並因此阻止垃圾收集)並在完成後將其從集合中刪除,然後完成被100%的時間激發,沒有內存泄漏。

只是我的兩分錢......

0

這個問題在WPF 4.5最近提高了它醜惡的頭對我。就我而言,它看起來像我的轉換在活動時收集垃圾,因此它有時從未觸發Completed事件,並且它從不重置其動畫。由於我的Checked VisualState基本上再次調用所有相同的屬性來將它們「修復」到它們的轉換終點,似乎這個狀態已經部分解僱,但我不相信它曾經做過。

解決方案:我在我的VisualTransitions中遺留了GeneratedDuration屬性(我的轉換運行速度比他們本來應該慢的速度慢,所以我放棄了嘗試加速它。)。我認爲這個屬性可以在給定時間「錨定」過渡。當我將屬性添加回轉換時,它解決了我的問題,並且我的動畫可以可靠地工作。

相關問題