C# 动画后执行命令

C# 动画后执行命令,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,我在屏幕上收集了错误,每一个错误一行。用户可以通过单击该行上的按钮来关闭任何错误消息。代码示例: <UserControl> <ItemsControl ItemsSource="{Binding Errors}" > <ItemsControl.ItemTemplate> <DataTemplate> <Grid x:Name="grid" Height="20

我在屏幕上收集了错误,每一个错误一行。用户可以通过单击该行上的按钮来关闭任何错误消息。代码示例:

<UserControl>
    <ItemsControl ItemsSource="{Binding Errors}" >
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid x:Name="grid" Height="20">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <TextBlock Text="{Binding ErrorText}"/>
                    <Button Grid.Column="1" Width="16" Height="16" Content="Close" Command="{Binding DataContext.RemoveErrorCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}}" CommandParameter="{Binding CurrentError}">
                        <Button.Triggers>
                            <EventTrigger RoutedEvent="ButtonBase.Click">
                                <BeginStoryboard>
                                    <Storyboard TargetProperty="Height" TargetName="grid">
                                        <DoubleAnimation To="0" Duration="0:0:0.35"/>
                                    </Storyboard>
                                </BeginStoryboard>
                            </EventTrigger>
                        </Button.Triggers>
                    </Button>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</UserControl>


问题是:正如您所看到的,我添加了带有情节提要的触发器,以明确说明问题,我希望平滑地隐藏消息,并且只有在关闭消息之后才能关闭它。因此,首先是故事板,然后执行命令。如何实现这一目标?请尽量减少代码隐藏。

在动画中使用已完成的事件处理程序

<Storyboard TargetProperty="Height" TargetName="grid">
       <DoubleAnimation To="0" Duration="0:0:0.35" Completed="do_this"/>
</Storyboard>

您需要为usercontrol及其所在的网格指定一个x:Name。我刚刚测试了这个,它确实有效,我不认为你能找到任何不接触代码背后的方法

您可以使用如下附加属性:

public static class StoryboardHelper
{
    public static readonly DependencyProperty CompletedCommandProperty = DependencyProperty.RegisterAttached("CompletedCommand", typeof(ICommand), typeof(StoryboardHelper), new PropertyMetadata(null, OnCompletedCommandChanged));
    public static readonly DependencyProperty CompletedCommandParameterProperty = DependencyProperty.RegisterAttached("CompletedCommandParameter", typeof(object), typeof(StoryboardHelper), new PropertyMetadata(null));

    public static void SetCompletedCommand(DependencyObject o, ICommand value)
    {
        o.SetValue(CompletedCommandProperty, value);
    }

    public static ICommand GetCompletedCommand(DependencyObject o)
    {
        return (ICommand)o.GetValue(CompletedCommandProperty);
    }

    public static void SetCompletedCommandParameter(DependencyObject o, object value)
    {
        o.SetValue(CompletedCommandParameterProperty, value);
    }

    public static object GetCompletedCommandParameter(DependencyObject o)
    {
        return o.GetValue(CompletedCommandParameterProperty);
    }

    private static void OnCompletedCommandChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var sb = sender as Storyboard;

        if(sb != null)
        {
            sb.Completed += (a, b) =>
            {
                var command = GetCompletedCommand(sb);

                if (command != null)
                {
                    if (command.CanExecute(GetCompletedCommandParameter(sb)))
                    {
                        command.Execute(GetCompletedCommandParameter(sb));
                    }
                }
            };
        }
    }
}
<Storyboard TargetProperty="Opacity" local:StoryboardHelper.CompletedCommand="{Binding Path=StoryCompletedCommand}">
    <DoubleAnimation From="0" To="1" Duration="0:0:5"/>
</Storyboard>
然后像这样使用它:

public static class StoryboardHelper
{
    public static readonly DependencyProperty CompletedCommandProperty = DependencyProperty.RegisterAttached("CompletedCommand", typeof(ICommand), typeof(StoryboardHelper), new PropertyMetadata(null, OnCompletedCommandChanged));
    public static readonly DependencyProperty CompletedCommandParameterProperty = DependencyProperty.RegisterAttached("CompletedCommandParameter", typeof(object), typeof(StoryboardHelper), new PropertyMetadata(null));

    public static void SetCompletedCommand(DependencyObject o, ICommand value)
    {
        o.SetValue(CompletedCommandProperty, value);
    }

    public static ICommand GetCompletedCommand(DependencyObject o)
    {
        return (ICommand)o.GetValue(CompletedCommandProperty);
    }

    public static void SetCompletedCommandParameter(DependencyObject o, object value)
    {
        o.SetValue(CompletedCommandParameterProperty, value);
    }

    public static object GetCompletedCommandParameter(DependencyObject o)
    {
        return o.GetValue(CompletedCommandParameterProperty);
    }

    private static void OnCompletedCommandChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        var sb = sender as Storyboard;

        if(sb != null)
        {
            sb.Completed += (a, b) =>
            {
                var command = GetCompletedCommand(sb);

                if (command != null)
                {
                    if (command.CanExecute(GetCompletedCommandParameter(sb)))
                    {
                        command.Execute(GetCompletedCommandParameter(sb));
                    }
                }
            };
        }
    }
}
<Storyboard TargetProperty="Opacity" local:StoryboardHelper.CompletedCommand="{Binding Path=StoryCompletedCommand}">
    <DoubleAnimation From="0" To="1" Duration="0:0:5"/>
</Storyboard>


因此,该命令将在动画之后触发。

为什么要进行向下投票?我不明白这怎么会不起作用,不是我。在这个事件处理程序中,我们无法执行任何操作—甚至DataContext是不可访问的,所以命令是必需的。此外,这是一种暗藏的方式,如果可能的话,我想避免它。我编辑了我的答案,为你们提供了一切你们应该需要的,让它工作。我不知道您以前为什么对该命令有问题,但我可以对已完成的事件做很多事情。不是我的DV,但可能是因为它不遵循MVVM(因为它使用代码隐藏)。是否有可能改用命令?在某些情况下,您可以使用命令,例如,如果您想更改可见性。但是要真正删除一个需要使用代码隐藏的控件,这个答案应该为您指明正确的方向:您可以保留现有的按钮触发器,并添加一个额外的交互触发器来调用您的命令。如果它起作用,那就太好了!尝试此操作后,它抛出了一个异常“触发器操作已被密封”。使用VS2015+.NET 4.5