Adding Global Behaviors to Silverlight Controls

Take a look at the two buttons below, and hover your mouse over them. You’ll notice that the buttons hide themselves.

However, when you take a look at the XAML, it is not obvious how this is implemented.

<UserControl x:Class="GlobalBehavior.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:local="clr-namespace:GlobalBehavior"
    mc:Ignorable="d"             
    d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        
        <Button Content="Click Me" Margin="36,28,0,0" HorizontalAlignment="Left" Width="112" Height="47" VerticalAlignment="Top" />
        <Button Content="Click Me" Margin="133,104,0,0" HorizontalAlignment="Left" Width="112" Height="47" VerticalAlignment="Top" />

    </Grid>
</UserControl>

This is done by attaching a behavior to the default Button style. There is a little wrinkle to the trick. The style setters by default is not able to set complex attached dependency properties. However, it can handle simple dependency properties. So, we’d write a simple depedency property that can be attached like this:


        <Style TargetType="Button">
            <Setter Property="local:HideButtonBehavior.ButtonBehaviors" Value="True" />
        </Style>

and the ButtonBehaviors dependency property sets the actual Behaviors on the button.

namespace GlobalBehavior
{
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Interactivity;
    using System.Windows.Media;
    using System.Windows.Media.Animation;

    public partial class HideButtonBehavior : Behavior<FrameworkElement>
    {        
        #region HideButtonBehavior.ButtonBehaviors depedency property

        public static bool GetButtonBehaviors(DependencyObject obj)
        {
            return (bool)obj.GetValue(ButtonBehaviorsProperty);
        }

        public static void SetButtonBehaviors(DependencyObject obj, bool value)
        {
            obj.SetValue(ButtonBehaviorsProperty, value);
        }

        // Using a DependencyProperty as the backing store for ButtonBehaviors. 
        public static readonly DependencyProperty ButtonBehaviorsProperty =
            DependencyProperty.RegisterAttached("ButtonBehaviors",
            typeof(bool), typeof(Button), new PropertyMetadata(false, ButtonBehaviorsChanged));

        private static void ButtonBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var behaviors = Interaction.GetBehaviors(d);
            var existingBehavior = behaviors.FirstOrDefault(b => b.GetType() ==
              typeof(HideButtonBehavior)) as HideButtonBehavior;
            if ((bool)e.NewValue == false &amp;&amp; existingBehavior != null)
            {
                behaviors.Remove(existingBehavior);
            }
            else if ((bool)e.NewValue == true &amp;&amp; existingBehavior == null)
            {
                /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
                /* !!! THE REST ARE ALL BOILERPLATE. THE REAL GOODNESS IS HERE !!! */
                behaviors.Add(new HideButtonBehavior());
                /* !!!                                                         !!! */
                /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
            }
        }

        #endregion

    }
}

and for completeness, here’s the code for the silly Button behavior.

namespace GlobalBehavior
{
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Interactivity;
    using System.Windows.Media;
    using System.Windows.Media.Animation;

    public partial class HideButtonBehavior : Behavior<FrameworkElement>
    {
        #region Custom Button Behavior

        protected override void OnAttached()
        {
            base.OnAttached();
            Button button = this.AssociatedObject as Button;
            button.RenderTransform = new ScaleTransform();
            button.MouseMove += button_MouseMove;
        }

        protected override void OnDetaching()
        {
            Button button = this.AssociatedObject as Button;
            button.MouseMove -= button_MouseMove;
            base.OnDetaching();
        }

        void button_MouseMove(object sender, MouseEventArgs e)
        {
            DoubleAnimationUsingKeyFrames anim = new DoubleAnimationUsingKeyFrames();
            anim.KeyFrames.Add(new LinearDoubleKeyFrame() { Value = 0.0, KeyTime = TimeSpan.FromMilliseconds(150) });
            anim.KeyFrames.Add(new LinearDoubleKeyFrame() { Value = 0.0, KeyTime = TimeSpan.FromMilliseconds(2000) });
            anim.KeyFrames.Add(new LinearDoubleKeyFrame() { Value = 1.0, KeyTime = TimeSpan.FromMilliseconds(2150) });

            Storyboard.SetTarget(anim, this.AssociatedObject);
            Storyboard.SetTargetProperty(anim, new PropertyPath("(UIElement.RenderTransform).(ScaleTransform.ScaleY)"));

            Storyboard sb = new Storyboard();
            sb.Children.Add(anim);
            sb.Begin();
        }
        #endregion

    }
}

Acknowledgements

This technique is first discovered by julmar