Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

SuspendedButton - How to suspend WPF Button's Click event

0.00/5 (No votes)
5 Oct 2011 2  
This tip shows how we can extend a Button control to support suspension of its Click event, in order to perform some animations before the Click event is raised.

When developing WPF views (XAML), we sometimes want to give the user an option to remove some elements from the view.

Usually, in order to achieve that, we bind the Command property of an ICommandSource (Button, MenuItem, etc...) to a Command (property with type that implements ICommand) in the view-model.

Sometimes, we want to perform an animation on the removed element when it is removed. Now, we have a problem. Suppose we have a button that is bound to a remove command in the view-model. When we click this button, the Click event is raised and the bound command is executed. After the command has been executed, the element that the animation is intended for, is already removed. So, if we know about the remove operation only when the button is clicked and, directly after the button has been clicked the element is removed, when can we perform the animation?

In order to solve this problem, we can extend the Button control by creating a class that derives from Button and, add the needed functionality, like the following:

public class SuspendedButton : Button
{
    #region SuspendTime
    public TimeSpan SuspendTime
    {
        get { return (TimeSpan)GetValue(SuspendTimeProperty); }
        set { SetValue(SuspendTimeProperty, value); }
    }
 
    public static readonly DependencyProperty SuspendTimeProperty =
        DependencyProperty.Register("SuspendTime", typeof(TimeSpan), typeof(SuspendedButton), new UIPropertyMetadata(TimeSpan.Zero));
    #endregion
 
    #region BeforeClick
    public static readonly RoutedEvent BeforeClickEvent = EventManager.RegisterRoutedEvent(
      "BeforeClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(SuspendedButton));
 
    public event RoutedEventHandler BeforeClick
    {
        add { AddHandler(BeforeClickEvent, value); }
        remove { RemoveHandler(BeforeClickEvent, value); }
    }
    #endregion
 
    protected override void OnClick()
    {
        RaiseEvent(new RoutedEventArgs(SuspendedButton.BeforeClickEvent));
 
        TimeSpan time = SuspendTime;
 
        ThreadPool.QueueUserWorkItem(new WaitCallback(o =>
        {
            Thread.Sleep(time);
            Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                            new ThreadStart(() =>
                            {
                                base.OnClick();
                            }));
        }));
    }
}

The SuspendTime property of this control can be used to tell how much time we have to wait between the actual click and the raise of the Click event. Here we can set the duration of the animation.

The BeforeClick event of this control is raised in the time of the actual click and, let us perform an animation before the Click event is raised.

For example, look at the following ItemsControl:

<ItemsControl ItemsSource="{Binding MyItems}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <DataTemplate.Resources>
                <Storyboard x:Key="hideItemStoryboard">
                    <DoubleAnimation Storyboard.TargetName="mainBorder"

                                     Storyboard.TargetProperty="(FrameworkElement.LayoutTransform).(ScaleTransform.ScaleY)"

                                     To="0"

                                     Duration="0:0:0.2" />
                </Storyboard>
            </DataTemplate.Resources>
            <Border x:Name="mainBorder"

                    BorderThickness="1"

                    BorderBrush="Black"

                    CornerRadius="3" 

                    Margin="2">
                <Border.LayoutTransform>
                    <ScaleTransform ScaleY="1" />
                </Border.LayoutTransform>
                <DockPanel Margin="2">
                    <local:SuspendedButton x:Name="btnRemove"

                                           Content="Remove"

                                           SuspendTime="0:0:0.2"

                                           DockPanel.Dock="Left"

                                           VerticalAlignment="Center"

                                           Command="{Binding RemoveCommand}" 

                                           Margin="5,0"/>
                    <TextBlock Text="{Binding Name}" />
                </DockPanel>
            </Border>
            <DataTemplate.Triggers>
                <EventTrigger RoutedEvent="local:SuspendedButton.BeforeClick"

                              SourceName="btnRemove">
                    <BeginStoryboard Storyboard="{StaticResource hideItemStoryboard}" />
                </EventTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

This ItemsControl has an ItemTemplate that contains: a Storyboard with an animation for the remove operation, a SuspendedButton that is bound to a remove command and, an EventTrigger that performs the remove animation when the BeforeClick event is raised. When the user clicks the remove button, the remove animation is performed and, then the element is removed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here