none
storyboardとbindingについて RRS feed

  • 質問

  • storyboardとbindingについて質問です。

    下記のようにxaml記述(storyboard)でscaleXなどのプロパティ(例えばVisibility)を変更する動きをつけたいです。triggerとなるプロパティ値を該当値に変更することで 1回動作することは確認しております。(triggerに使用するプロパティ(Windowのopacityやvisibility)やscaleXはViewModelでbindingしておりますのでxamlプロパティ値の変更はボタン押下契機でViewModelを通して行っています。)

    ただ、2度目以降同様の処理を行っても動きがありません。これはstoryboadで指定したbindingのせいなのでしょうか?(bindingが切れた?)

    代替策や原因が何かあれば教えて頂けると助かります。

    ちなみに<Setter Property="Visibility" Value="Collapsed" />はtrigger内(★)では効かなかったのですが、仕様なのでしょうか?

    xaml

    <Trigger Property="Opacity" Value="0.25">

    <!-- ★-->

                            <Trigger.EnterActions>
                                <BeginStoryboard>
                                    <Storyboard>
    <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" From="1" To="0.05" Duration="0:0:0.5" />
                                    </Storyboard>
                                </BeginStoryboard>
                            </Trigger.EnterActions>
                        </Trigger>



    • 編集済み tenpu 2014年4月7日 6:06
    2014年4月7日 2:45

回答

  • 原因は追い切れてませんが、DataTriggerを2つ並べてそれぞれにEnterActionsを記述すると望ましくない動作をするようです。

    二つのDataTriggerのそれぞれのEnterActionsで記述するのではなく、私が書いたように一つのDataTriggerのEnterActionsとExitActionsで記述してください。

    • 回答としてマーク tenpu 2014年4月8日 7:32
    2014年4月8日 6:17

すべての返信

  • 下記のようにxaml記述(storyboard)でscaleXなどのプロパティ(例えばVisibility)を変更する動きをつけたいです。triggerとなるプロパティ値を該当値に変更することで 1回動作することは確認しております。(triggerに使用するプロパティ(opacityやvisibility)やscaleXはViewModelでbindingしておりますのでxamlプロパティ値の変更はボタン押下契機でViewModelを通して行っています)

    ただ、2度目以降同様(例.scaleXを0.2に変化させる)の処理を行っても動きがありません。これはstoryboadで指定したbindingのせいなのでしょうか?(bindingが切れた?)

    Opacityを契機とするTriggerですよね。つまり、ButtonのOpacityの値が0.25なり0.3なりになったら指定されたアニメーションが動作する記述です。

    で、「同様の処理」ってなんでしょう? 「ScaleXを0.2に変化させる」はトリガーとは無関係ですから、それは「同様の処理」ではないはずですが。

    ちなみに<Setter Property="Visibility" Value="Collapsed" />はtrigger内(★)では効かなかったのですが、仕様なのでしょうか?

    依存関係プロパティ値の優先順位 を確認して、スタイルのトリガー(かな?)よりも優先順位の高い方法でVisiblityを設定していませんか?

    2014年4月7日 4:57
  • 2度目以降も同様に動作するはずです。念のため、こちらで動作確認の簡単なサンプルを作って確かめてみました。Opacityを0.25にする度に、何度でもアニメーションが実行されます。
    しかし、このサンプルはtenpuさんが実際に書かれたコードとは違います。動作しない最低限のコードを可能であれば掲示していただけませんか?
    私もHongliangさんと同じで、「2度目以降同様(例.scaleXを0.2に変化させる)」の意味がわかりませんでした。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2014年4月7日 5:51
    モデレータ
  • Hongliangさん

    回答ありがとうございます。

    Opacityを契機とするTriggerですよね。つまり、ButtonのOpacityの値が0.25なり0.3なりになったら指定されたアニメーションが動作する記述です。で、「同様の処理」ってなんでしょう? 「ScaleXを0.2に変化させる」はトリガーとは無関係ですから、それは「同様の処理」ではないはずですが。

    仰る通り紛らわしいので、2つ目のtriggerは削除しました。質問作成中のサンプルxamlに元々From,Toを記述していなかったため、2回目以降変化しないように(見えることを)誤解されるのではないかと考えて紛らわしい記述になってしまいました。

    構造としては、xaml内に別途ボタンがあって、ボタン押下時のCommand処理内でViewModelのBinding値(WindowのOpacity値)を変更しています。(他のプロパティも同様)

    xaml(A)内に別のxaml(B)を配置(参照)しており、Opacityの変更はxaml(B)に対してのものです。

    質問に挙げましたtriggerはxaml(A)内で記述し、style指定でxaml(B)が参照しています。

    <Window x:Class= ~           ・・・         xmlns:sample="clr-namespace:・・・"> <Window.Resources> <ResourceDictionary> <Style x:Key="hogeStyle" TargetType="UserControl"> <Style.Triggers> <Trigger Property="Opacity" Value="0.25"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" From="1" To="0.05" Duration="0:0:0.1" /> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" > <DiscreteDoubleKeyFrame KeyTime="0:0:1" Value="0.1" /> </DoubleAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> </Trigger> <Trigger Property="Opacity" Value="0.1"> <Setter Property="Visibility" Value="Collapsed" /> </Trigger> <Trigger Property="Opacity" Value="1"> <!--triggerとSetterは逆でも構いません--> <Setter Property="Visibility" Value="Visible" /> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" From="0.05" To="1" Duration="0:0:0.1" /> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> </Trigger> </Style.Triggers> </Style> </ResourceDictionary> </Window.Resources> <button Command=・・・ /> <!--commandでopacityを1にします。--> <sample:XamlB x:Name="SampleWindow" Style="{StaticResource hogeStyle}" Visibility="{Binding WindowModel.Visibility}" Opacity="{Binding WindowModel.Opacity}"/> <!--XamlBはボタン1つ、xamlAと同じサイズで最前面に表示します。そのボタン押下時(command処理時)に自身のopacityを0.25にします--> <!- XamlBのVisibility初期値はCollapsedです -->

    <!-処理フロー:(A)のボタン押下→(B)が表示→(B)のボタン押下→(B)がアニメーションで消え、下層にいる(A)が表示--> </Window>


    依存関係プロパティ値の優先順位 を確認して、スタイルのトリガー(かな?)よりも優先順位の高い方法でVisiblityを設定していませんか?

    情報提供ありがとうございます。

    スタイル関係のプロパティは低めの順位なのですね。ちなみにxaml(B)のVisibilityをViewModel内の値でbindingしている場合は、3.ローカル値辺りに該当するのでしょうか?解読しきれませんでした。




    • 編集済み tenpu 2014年4月7日 8:07
    2014年4月7日 6:04
  • 実現したいUIは、以下のようなものということで良いでしょうか?

    • 2つのUI要素が重なっている。奥側をA、手前側をBとする。
    • Bをクリックすれば(あるいは何かのイベントなどで)、BのOpacityおよびRenderTransformを消える方向にアニメーションさせてAを操作できるようにする。
    • Aをクリックすれば(あるいは何かのイベントなどで)、BのOpacityおよびRenderTransformを現れる方向にアニメーションさせてAを隠すようにする。

    こういうUIであるとして、なぜVMにOpacity(にバインドさせる値)を持たせているのでしょうか。表示非表示を切り替えるUIであるなら、VMに持たせるのは「(Aを)表示しているかしていないか」というbool型のフラグのみで十分ではないでしょうか。

    ここまで問題ないとすると、DataTriggerを使ってVMのbool値でバインドし、BのOpacity、RenderTransform、VisibilityなどをEnterActionsおよびExitActionsで操作するだけで上記UIは実現できます(Bが消えるときのVisibilityの操作は、BeginTimeをOpacity等の操作が完了したときのタイミングにすることで表現できます)。

    なお、tenpuさんのお書きになったXAMLがtenpuさんの想定外の動作を示すかという話ですが、

    • アニメーションによる依存関係プロパティの変更はバインディングソースとは無関係
    • アニメーションによる依存関係プロパティの変更はほとんどのほかの方法による変更よりも優先度が高い

    という二つの理由によるものです。つまり、アニメーションが発生してもVMのOpacityにバインドしている値は変更されず、またOpacityにバインドしている値を変更しても、アニメーションによる依存関係プロパティの変更が存在しているのならVMのバインディングによる値は使用されません。

    スタイル関係のプロパティは低めの順位なのですね。ちなみにxaml(B)のVisibilityをViewModel内の値でbindingしている場合は、3.ローカル値辺りに該当するのでしょうか?解読しきれませんでした。

    バインドしているかどうかは重要ではありません。要素のプロパティに直接設定している(ローカル値)とか、スタイルで設定している(スタイルのSetter)とか、そういうのです。

    <Button Content="{Binding}"/>

    なら、Contentにはローカル値が設定されています。

    <Button Content="OK"/>

    でも同じです。

    <Button>
      <Button.Style>
        <Style ...>
          <Setter Property="Content" Value="{Binding}"/>
    ならスタイルのSetterが設定されています。じゃあ
    <Button Content="OK">
      <Button.Style>
        <Style ...>
          <Setter Property="Content" Value="{Binding}"/>
    なら?というとき、先述の優先順位の話になります。

    • 編集済み Hongliang 2014年4月7日 8:50 Button.Styleが抜けていたので追加ついでにXAMLを整形
    2014年4月7日 8:41
  • 実現したいUIは、以下のようなものということで良いでしょうか?

    • 2つのUI要素が重なっている。奥側をA、手前側をBとする。
    • Bをクリックすれば(あるいは何かのイベントなどで)、BのOpacityおよびRenderTransformを消える方向にアニメーションさせてAを操作できるようにする。
    • Aをクリックすれば(あるいは何かのイベントなどで)、BのOpacityおよびRenderTransformを現れる方向にアニメーションさせてAを隠すようにする。

    こういうUIであるとして、なぜVMにOpacity(にバインドさせる値)を持たせているのでしょうか。表示非表示を切り替えるUIであるなら、VMに持たせるのは「(Aを)表示しているかしていないか」というbool型のフラグのみで十分ではないでしょうか。

    ここまで問題ないとすると、DataTriggerを使ってVMのbool値でバインドし、BのOpacity、RenderTransform、VisibilityなどをEnterActionsおよびExitActionsで操作するだけで上記UIは実現できます(Bが消えるときのVisibilityの操作は、BeginTimeをOpacity等の操作が完了したときのタイミングにすることで表現できます)。

    Hongliangさん

    詳細な回答ありがとうございます。

    実現したいUIはHongliangさんが記述された内容と相違ありません。また、おっしゃる通りOpacityである必要はありません。手っ取り早く既定のプロパティをトリガーに選んだだけです。

    試しにDataTriggerを使ってVMのbool値(IsDisplayed)でバインドして動かしてみましたが、結果は同じく2回目以降の動作がなされませんでした。

    動作遷移:①xaml(A)前面に表示・ボタン押下→②xaml(B)前面に表示→③xaml(B)ボタン押下→変化なし(Bが前面のまま)

    他にも①xaml(B)を表示するAnimationを削除(Animation無しでBを表示)すると、xaml(B)押下時のAnimationは動作し、xaml(A)が前面に表示されます。

    ただし、これも2回目以降のxaml(B)押下時のAnimationが動きません。1度も動かないならまだしも1度だけ動くというのはxaml以外にも問題があるのですかね?

    Style.Triggers以下を下記のように変更してみました。

    <Style.Triggers> <DataTrigger Binding="{Binding WindowModel.IsDisplayed}" Value="true"> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" From="1" To="0" Duration="0:0:0.5" /> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleY)" From="1" To="0" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> </DataTrigger> <DataTrigger Binding="{Binding WindowModel.IsDisplayed}" Value="false"> <DataTrigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleX)" From="0" To="1" Duration="0:0:0.5" /> <DoubleAnimation Storyboard.TargetProperty="RenderTransform.(ScaleTransform.ScaleY)" From="0" To="1" Duration="0:0:0.5" /> </Storyboard> </BeginStoryboard> </DataTrigger.EnterActions> </DataTrigger>

    </Style.Triggers>

    バインドしているかどうかは重要ではありません。要素のプロパティに直接設定している(ローカル値)とか、スタイルで設定している(スタイルのSetter)とか、そういうのです。

    例示頂いた内容でだいたい理解できました。今回はxaml上で直接設定している値はないので、関係なさそうですね。

    2014年4月8日 5:05
  • 原因は追い切れてませんが、DataTriggerを2つ並べてそれぞれにEnterActionsを記述すると望ましくない動作をするようです。

    二つのDataTriggerのそれぞれのEnterActionsで記述するのではなく、私が書いたように一つのDataTriggerのEnterActionsとExitActionsで記述してください。

    • 回答としてマーク tenpu 2014年4月8日 7:32
    2014年4月8日 6:17
  • Hongliangさん

    ありがとうございます。一つのDataTriggerで記述したところ、繰り返し動作することを確認できました。

    今回はbool(2分岐)なので必要ないですが、あるプロパティに対する動作分岐が3種類以上になった場合はどうするべきか気になりました。

    2014年4月8日 7:37