18

次の問題は何日も私を悩ませてきました、しかし私はそれを最も単純な形に蒸留することができただけです。次の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バックグラウンドが状態に依存するように、コントロールを再テンプレート化するだけです。

  • チェック済み=緑
  • チェックなし=赤
  • チェック(遷移)=ライトグリーン
  • チェックを外す(遷移)=明るい赤

したがって、チェックボックスの1つをオンにすると、短時間は薄緑色に変わり、その後緑色に変わることが期待されます。同様に、チェックを外すと、短時間は明るい赤に変わり、その後赤に変わることが期待されます。

そしてそれは通常まさにそれをします。しかしいつもではない。

プログラムを十分に長くプレイすると(約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のバグだと思いますか?この問題の既知の説明または回避策はありますか?

4

4 に答える 4

16

私は次のように問題を特定して修正することができました:

まず、再現プロジェクトを.NET 3.5にダウングレードし、CodePlexからWPFToolkitのソースコードを取得しました。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グループに調査結果を投稿していました。この時点で、PavanPodilaがこの宝石で応答しました。

ケント、

ストーリーボードが完了したイベントを起動しないという問題が過去に発生しました。私が気付いたのは、ストーリーボードを最初に停止せずに直接置き換えると、いくつかの順序が正しくない完了イベントが表示される可能性があるということです。私の場合、以前のストーリーボードを停止せずに、同じ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のアニメーションサブシステムのバグまたは奇妙な動作を回避します。

于 2011-02-17T19:25:13.940 に答える
2

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-02-15T14:45:39.190 に答える
0

それがあなたの問題に関連しているかどうかはわかりませんが、AnimationClockの問題にも遭遇しました。実行中のアニメーションを別のアニメーションに置き換えるときに確実に起動されませんでした。ガベージコレクションと参照/ルート化の問題だと思いました。AnimationClockがまだ実行されているが、何らかの形で参照されなくなった場合、いつでもガベージコレクションされる可能性があります。ガベージコレクションが発生する前に終了に達した場合、Completedが発生します。それ以外の場合は発生しません。これは非常に予測不可能な動作になります。

私の回避策は、最初に時計をコレクションに追加し(ルート化してガベージコレクションを防ぐため)、Completed時にコレクションから削除することです。その後、Completedが100%起動され、メモリリークは発生しません。

ちょうど私の2セント...

于 2011-09-22T19:46:54.467 に答える
0

この問題は、最近WPF4.5で私にとって醜い頭を上げました。私の場合、トランジションがアクティブなときにガベージコレクションを取得していたように見えるため、Completedイベントが発生せず、アニメーションがリセットされないことがありました。私のCheckedVisualStateは、基本的にすべて同じプロパティを再度呼び出して、遷移エンドポイントでそれらを「修正」したため、この状態が部分的に発生したように見えましたが、これまでに発生したとは思いません。

解決策:VisualTransitionsのGeneratedDurationプロパティを省略していました(トランジションの実行速度が本来よりも遅いため、速度を上げるために省略しました)。このプロパティは、特定の時間の遷移を「アンカー」するために機能すると思います。プロパティをトランジションに追加し直すと、問題が修正され、アニメーションが確実に機能するようになりました。

于 2014-05-13T17:13:22.023 に答える