ストーリーボードに添付された動作にバインドする
-
22-07-2019 - |
質問
Storyboard Completedイベントが発生したときにViewModelでメソッドを呼び出せるようにするために、Storyboardの添付依存プロパティを作成しました。
public static class StoryboardExtensions
{
public static ICommand GetCompletedCommand(DependencyObject target)
{
return (ICommand)target.GetValue(CompletedCommandProperty);
}
public static void SetCompletedCommand(DependencyObject target, ICommand value)
{
target.SetValue(CompletedCommandProperty, value);
}
public static readonly DependencyProperty CompletedCommandProperty =
DependencyProperty.RegisterAttached(
"CompletedCommand",
typeof(ICommand),
typeof(StoryboardExtensions),
new FrameworkPropertyMetadata(null, OnCompletedCommandChanged));
static void OnCompletedCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
Storyboard storyboard = target as Storyboard;
if (storyboard == null) throw new InvalidOperationException("This behavior can be attached to Storyboard item only.");
storyboard.Completed += new EventHandler(OnStoryboardCompleted);
}
static void OnStoryboardCompleted(object sender, EventArgs e)
{
Storyboard item = ... // snip
ICommand command = GetCompletedCommand(item);
command.Execute(null);
}
}
次に、バインド構文を使用してXAMLで使用しようとします。
<Grid>
<Grid.Resources>
<Storyboard x:Key="myStoryboard" my:StoryboardExtensions.CompletedCommand="{Binding AnimationCompleted}">
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0" Duration="0:0:5" />
</Storyboard>
<Style x:Key="myStyle" TargetType="{x:Type Label}">
<Style.Triggers>
<DataTrigger
Binding="{Binding Path=QuestionState}" Value="Correct">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource myStoryboard}" />
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Label x:Name="labelHello" Grid.Row="0" Style="{StaticResource myStyle}">Hello</Label>
</Grid>
これは次の例外で失敗します。
System.Windows.Markup.XamlParseExceptionが発生しました Message =&quot;属性「Style」の値をタイプ「System.Windows.Style」のオブジェクトに変換できません。スレッド間で使用するために、このStoryboardタイムラインツリーをフリーズすることはできません。マークアップファイル「TestWpfApp; component / window1.xaml」のオブジェクト「labelHello」でエラーが発生しました
ストーリーボードの添付されたICommandプロパティで動作するバインディング構文を取得する方法はありますか?
解決 3
この問題を回避するために、Storyboard Helpers(ここにソースコード)。ストーリーボード自体にそれらをアタッチすることをあきらめ、ストーリーボードが完了したときにViewModelでICommandを呼び出すために(任意の)フレームワーク要素にアタッチし、ViewModelの特定のイベントにバインドしてストーリーボードを起動します。 3番目の添付プロパティは、処理するストーリーボードを指定します。
<FrameworkElement
my:StoryboardHelpers.Storyboard="{StaticResource rightAnswerAnimation}"
my:StoryboardHelpers.Completed="{Binding CompletedCommand}"
my:StoryboardHelpers.BeginEvent="{Binding StartCorrectAnswer}" />
他のヒント
これは仕様によるものです。スタイルに配置されたフリーズ可能なオブジェクトがある場合、スタイルは固定され、クロススレッドアクセスが可能になります。ただし、バインディングは基本的に式であり、データバインディングはシングルスレッドであるため凍結できません。
これを行う必要がある場合は、スタイルではなくフレームワーク要素の下のスタイルの外側にトリガーを配置します。これは、Grid.Triggersセクションで実行できます。スタイルが完全ではなく、トリガーを複製する必要があるため、これは少し厄介ですが、それは「設計による」ものです。 WPFの機能。
MSDN Socialフォーラムの完全な回答はこちら。
新しいFreezableから派生したクラスを作成して、ストーリーボードをシムとして起動できます。そのshimオブジェクトのプロパティをストーリーボード名にバインドします。そうすれば、トリガーを複製したり、スタイルの外部に保存したりする必要がなくなります。