Click here to Skip to main content
15,867,895 members
Articles / Desktop Programming / WPF
Tip/Trick

SuspendedButton - How to suspend WPF Button's Click event

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
5 Oct 2011CPOL2 min read 18K   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:

C#
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:

XML
<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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
-- There are no messages in this forum --