Vibration Behaviors for Windows Phone – Part 1

So I got my wife an HTC Titan for Christmas and I loved the way it vibrates compared to my Focus. It just feels nice with much lower frequency, while the vibrations on the Focus are a bit like an electric shock. This got me thinking that it would be nice to have the same sort of haptic feedback you get on the 3 hardware buttons on the regular Silverlight buttons. While you could just handle the touch events on every button you choose – it is a lot of code that just gets duplicated all over your app and it is hard to update it globally – e.g. if you want to allow the user to switch it off everywhere or automatically preconfigure it when the app first runs based on the device used. The best solution for it seems to be to create a Behavior that you can easily attach to any button, so that is what I had set off to create.

ButtonVibrationBehavior

Implementation of the behavior for the button can be pretty straightforward. One thing of note is doing the vibration just when the button gets clicked does not feel like a good feedback because it only happens when you remove your finger from the button, while feedback when you first touch the button is what really makes it feel more real. I tried using the MouseLeftButtonDown event to handle it, but it seems like it gets intercepted by the button itself or something in its template. What works is ManipulationStarted. The regular Button.Click event works well for when you release the button, so if you do click the button you have two events – one when you touch it and one when you release it, while you only get one if you slide your finger out of the button area if you change your mind about clicking it after you had already touched it.
To use behaviors you need to reference the System.Windows.Interactivity assembly and use the namespace of the same name. By deriving your class from Behavior you get the AssociatedObject of type Button and you can add the VibrateController calls to the event handlers that you attach in the OnAttached override and remove in the OnDetaching override.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
using Microsoft.Devices;

namespace VibrationBehaviors
{
    public class ButtonVibrationBehavior : Behavior<Button>
    {
        #region Duration
        /// <summary>
        /// Duration Dependency Property
        /// </summary>
        public static readonly DependencyProperty DurationProperty =
            DependencyProperty.Register(
                "Duration",
                typeof(TimeSpan),
                typeof(ButtonVibrationBehavior),
                new PropertyMetadata(TimeSpan.FromMilliseconds(20)));

        /// <summary>
        /// Gets or sets the Duration property. This dependency property 
        /// indicates the duration of the vibration.
        /// </summary>
        public TimeSpan Duration
        {
            get { return (TimeSpan)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }
        #endregion

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.ManipulationStarted += OnButtonManipulationStarted;
            AssociatedObject.Click += this.OnButtonClicked;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.ManipulationStarted -= OnButtonManipulationStarted;
            AssociatedObject.Click -= this.OnButtonClicked;
        }

        private void OnButtonManipulationStarted(object sender, ManipulationStartedEventArgs e)
        {
            VibrateController.Default.Start(this.Duration);
        }

        private void OnButtonClicked(object sender, RoutedEventArgs e)
        {
            VibrateController.Default.Start(this.Duration);
        }
    }
}

Using the behavior is fairly straightforward – you need to reference System.Windows.Interactivity and the assembly where you define the behavior and it should be easy to use in Blend.

<Button
    Content="ButtonVibrationBehavior"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:v="clr-namespace:VibrationBehaviors;assembly=VibrationBehaviors">
    <i:Interaction.Behaviors>
        <v:ButtonVibrationBehavior
            Duration="0:0:0.02" />
    </i:Interaction.Behaviors>
</Button>

Vibration.VibratingButton Attached Property

While using behaviors in Blend is natural and easy – I do not use Blend too much, since I feel I can control the layout better by just typing it out in XAML and typing out a behavior is just a bit too verbose – especially if you have a lot of controls that all need that behavior set. That is why I usually prefer to implement behaviors simply as attached dependency properties, so instead of the XAML above I can do

<Button
    Content="Vibration.VibratingButton"
    v:Vibration.VibratingButton="True" />

The initial implementation I have is just a wrapper around the behavior:

using System.Windows;
using System.Windows.Interactivity;

namespace VibrationBehaviors
{
    public class Vibration
    {
        #region VibratingButton
        /// <summary>
        /// VibratingButton Attached Dependency Property
        /// </summary>
        public static readonly DependencyProperty VibratingButtonProperty =
            DependencyProperty.RegisterAttached(
                "VibratingButton",
                typeof(bool),
                typeof(Vibration),
                new PropertyMetadata(false, OnVibratingButtonChanged));

        /// <summary>
        /// Gets the VibratingButton property. This dependency property 
        /// indicates whether the button should vibrate.
        /// </summary>
        public static bool GetVibratingButton(DependencyObject d)
        {
            return (bool)d.GetValue(VibratingButtonProperty);
        }

        /// <summary>
        /// Sets the VibratingButton property. This dependency property 
        /// indicates whether the button should vibrate.
        /// </summary>
        public static void SetVibratingButton(DependencyObject d, bool value)
        {
            d.SetValue(VibratingButtonProperty, value);
        }

        /// <summary>
        /// Handles changes to the VibratingButton property.
        /// </summary>
        /// <param name="d">
        /// The <see cref="DependencyObject"/> on which
        /// the property has changed value.
        /// </param>
        /// <param name="e">
        /// Event data that is issued by any event that
        /// tracks changes to the effective value of this property.
        /// </param>
        private static void OnVibratingButtonChanged(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            bool newVibratingButton = (bool)d.GetValue(VibratingButtonProperty);

            var behaviors = Interaction.GetBehaviors(d);

            if (newVibratingButton)
            {
                for (int i = 0; i < behaviors.Count; i++)
                {
                    if (behaviors[i] is ButtonVibrationBehavior)
                    {
                        return;
                    }
                }

                behaviors.Add(new ButtonVibrationBehavior());
            }
            else if (!newVibratingButton)
            {
                for (int i = 0; i < behaviors.Count;)
                {
                    if (behaviors[i] is ButtonVibrationBehavior)
                    {
                        behaviors.RemoveAt(i);
                        continue;
                    }

                    i++;
                }
            }
        }
        #endregion
    }
}

Next Steps

While this post shows how to get a basic behavior to work – there are is lots of room for improvement here and I am already thinking on another post to make it better.

While the attached property implementation using the behaviors works fine, you get the incurred cost of loading the another assembly (System.Windows.Interactivity), so if you otherwise do not use it – it would be good to have the property defined without using behaviors at all.

Another thing is – if these vibrations turn out to work well for regular buttons – it might make sense to have another behavior/property for ApplicationBar buttons/menu items, checkboxes, ListBoxes, ContextMenu items etc.

Lastly, it would be good to do some research on how these vibrations work as haptic feedback on different phones and determine defaults for vibration duration depending on device/manufacturer and add a global on/off switch to allow users to easily enable/disable the feedback. I hear and realize many people might not like it and want to switch it off.

Sources

You can download the source code from here.

Related posts

Advertisements
Tagged , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: