绑定到Storyboard上的附加行为

3

我创建了一个附加依赖属性,用于处理 Storyboards,目的是在 Storyboard 完成事件触发时调用 ViewModel 上的方法:

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="无法将属性 'Style' 中的值转换为类型 'System.Windows.Style' 的对象。无法冻结此Storyboard时间线树以跨线程使用。 在标记文件 'TestWpfApp;component/window1.xaml' 中的对象 'labelHello'

是否有办法通过附加的ICommand属性使Binding语法与Storyboard正常工作?

3个回答

1

这是一种设计。如果您有一个可冻结的对象放入样式中,样式将被冻结以允许跨线程访问。但是,您的绑定本质上是一个表达式,这意味着它不能被冻结,因为数据绑定是单线程的。

如果您需要这样做,请将触发器放在框架元素下的样式之外。您可以在Grid.Triggers部分中执行此操作。这有点糟糕,因为您的样式不再完整,您必须复制触发器,但这是WPF中的“按设计”功能。

MSDN社交论坛上的完整答案在此处


2
但是Grid.Triggers不接受DataTrigger,只接受EventTrigger。 - Mark Heath
嗯,这让事情变得更加复杂了。那我就不太确定了。 - Ray Booysen
这个想法仍然存在,关于可冻结对象,但解决方案?不确定。 - Ray Booysen

0
为了解决这个问题,我创建了一堆附加属性,称为Storyboard Helpers(源代码在此)。我放弃了尝试将它们附加到Storyboard本身,并现在将它们附加到任何(任意的)框架元素上,在Storyboard完成时调用ViewModel上的ICommand,以及绑定到ViewModel上的特定事件来启动Storyboard。第三个附加属性指定我们正在处理的Storyboard:
<FrameworkElement
   my:StoryboardHelpers.Storyboard="{StaticResource rightAnswerAnimation}"
   my:StoryboardHelpers.Completed="{Binding CompletedCommand}"
   my:StoryboardHelpers.BeginEvent="{Binding StartCorrectAnswer}" />

请尝试此链接:http://learninggames.codeplex.com/SourceControl/changeset/view/7a81670b0a63#LearningGames.Framework%2fStoryboardHelpers.cs - Curt Nichols

0

你可以创建一个新的 Freezable 派生类作为 shim 来启动 storyboard。将 shim 对象上的属性绑定到 storyboard 名称上。这样,您就不必在样式之外重复触发器或存储它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接