1

I have collection of errors on the screen, one line per one error. User can close any error message by clicking the button on that row. Code example:

<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>

What's the problem: as you can see, i added trigger with storyboard to make it clear, i want to smoothly hide the message, and only after to close it. So it will be, first storyboard, then executing the command. How can it be achieved? As less codebehind as possible, please.

cdmnk
  • 314
  • 1
  • 11
  • 1
    This answer should point you in the right direction: http://stackoverflow.com/a/14124760/1231132 You can keep your existing Button trigger, and add an additional Interactivity trigger to invoke your command. – Sean Beanland May 04 '15 at 14:57

2 Answers2

1

You could use an attached Property like this:

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));
                    }
                }
            };
        }
    }
}

and use it like this:

<Storyboard TargetProperty="Opacity" local:StoryboardHelper.CompletedCommand="{Binding Path=StoryCompletedCommand}">
    <DoubleAnimation From="0" To="1" Duration="0:0:5"/>
</Storyboard>

So the Command will be triggered after the animation.

bolia
  • 131
  • 7
  • This would be nice if it worked! Tried this and it threw an exception "Trigger action is already sealed". Using VS2015 + .NET 4.5 – Contango Apr 21 '16 at 15:40
0

Use the Completed event handler in your DoubleAnimation

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

You will have to set the event in the code behind but it's very simple for what you need to do.

EDIT: this is the event handler you should get in the codebehind and the line of code you need to remove the usercontrol

private void do_this(object sender, EventArgs e)
{
    this.Grid.Children.Remove(UserControl);
}

you'll need to have a x:Name for the usercontrol and the grid that it is in. I just tested this and it does work, I don't think you can find any way of doing this without touching the codebehind.

CJK
  • 952
  • 2
  • 10
  • 22
  • Why the downvote? I don't see how this wouldn't work. – CJK May 04 '15 at 13:44
  • Not from me. In this event handler, we couldn't do anything - even DataContext is inaccessible, so as required command. Also it's codebehind way, i want to avoid it if possible – cdmnk May 04 '15 at 13:48
  • I edited my answer to give you everything you should need to get that working. I don't know why you had a problem with that command before but I could do quite a bit with the completed event. – CJK May 04 '15 at 14:32
  • not my DV but maybe it is because it is not following MVVM (since it uses code behind). Any possibility of using Commands instead? – default May 04 '15 at 14:48
  • In some cases you could use that, for example if you want to change the visibility. but to actually remove a control that requires using the code behind. – CJK May 04 '15 at 14:58
  • Sorry for possible misunderstanding, but i don't need to remove control. I use ObservableCollection, and i want to remove item [error] from that collection, not from visual tree. – cdmnk May 04 '15 at 15:07
  • the same code should work just add the x:name attribute to the usercontrol and the item you're removing if you'd like i can update my answer to reflect that. – CJK May 04 '15 at 19:17
  • i'd be at least interested to know how to get 'current' error from this event handler, because sender doesn't contain any information – cdmnk May 05 '15 at 06:23