martes, 16 de noviembre de 2010

HoldPressingClick Behavior

I had to design a new behavior in order to capture the pressing click action along a time. Furthermore, when this action performs, I needed to change a property of other element. Finally, I wanted to do all this in a resource dictionary (without code behind, according with MVVM).

I did this work with components (I love the component programming).

Trigger

I knew a trigger in Microsoft Blend named "ChangePropertyAction" (assembly "Microsoft.Expression.Interactions.dll") that is executed when an event is raised. But I had some difficulties in order to run it programmatically, so I inherited to this. I named it as "ManualChangePropertyAction" and it is quite simple, I just created the constructor with the required parameters, attached the object that will have the behavior and published a new method "Execute" to run the trigger.

    using System.Windows;
    using Microsoft.Expression.Interactivity.Core;

    public class ManualChangePropertyAction : ChangePropertyAction
    {
        public ManualChangePropertyAction(UIElement associateObject, object targetObject, string propertyName, object value)
        {
            this.Attach(associateObject);

            this.TargetObject = targetObject;
            this.PropertyName = propertyName;
            this.Value = value;
        }

        public void Execute()
        {
            this.Invoke(this.Value);
        }
    }


Behavior

Finally, I created the behavior pretty simple also. I have published four properties:

1.- TargetObject: The target object that will be affected by the behavior.
2.- PropertyName: The property that will change.
3.- Value: The new value for the specified property.
4.- MillisesondsForHoldClick: Number of milliseconds in order to consider the click action as a hold pressing action.

In the OnAttached and OnDetaching methods (from the Behavior<T> that inherit to), we just attach the event of "MouseLeftButtonDown" and "MouseLeftButtonUp". The first one initializes a datetime with the current time and the second one checks whether the elapsed time is greater than the MillisecondsForHoldClick property and, in this case, execute the above trigger:

    /// <summary>
    /// This behavior runs when the user has performed a hold click.
    /// The specified property of the pointed target object will get changed with the value property.
    /// </summary>
    public class HoldClickBehavior : Behavior<UIElement>
    {
        /// <summary>
        /// Target element property.
        /// </summary>
        public static readonly DependencyProperty TargetObjectProperty = DependencyProperty.Register("TargetObject",
            typeof(object), typeof(HoldClickBehavior),
            null);

        /// <summary>
        /// Property name property.
        /// </summary>
        public static readonly DependencyProperty PropertyNameProperty = DependencyProperty.Register("PropertyName",
            typeof(string), typeof(HoldClickBehavior),
            null);

        /// <summary>
        /// Value of the property of the target element.
        /// </summary>
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
            typeof(object), typeof(HoldClickBehavior),
            null);

        /// <summary>
        /// Value to specify the milliseconds in order to consider the click action as a hold action.
        /// </summary>
        public static readonly DependencyProperty MillisesondsForHoldClickProperty = DependencyProperty.Register("MillisesondsForHoldClick",
            typeof(int), typeof(HoldClickBehavior),
            new PropertyMetadata(1000));

        /// <summary>
        /// Property for the target object.
        /// </summary>
        public object TargetObject
        {
            get { return (object)GetValue(TargetObjectProperty); }
            set { SetValue(TargetObjectProperty, value); }
        }

        /// <summary>
        /// Property for the property name.
        /// </summary>
        public string PropertyName
        {
            get { return (string)GetValue(PropertyNameProperty); }
            set { SetValue(PropertyNameProperty, value); }
        }

        /// <summary>
        /// Property for the new value of the property.
        /// </summary>
        public object Value
        {
            get { return (object)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        /// <summary>
        /// Property to specify the milliseconds in order to consider the click action as a hold action.
        /// </summary>
        public int MillisesondsForHoldClick
        {
            get { return (int)GetValue(MillisesondsForHoldClickProperty); }
            set { SetValue(MillisesondsForHoldClickProperty, value); }
        }

        /// <summary>
        /// Field to know when the user has clicked.
        /// </summary>
        private DateTime clickDownDateTime;

        /// <summary>
        /// Attach the specified element where the behavior will start.
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();

            // Register event handlers
            this.AssociatedObject.MouseLeftButtonDown += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
            this.AssociatedObject.MouseLeftButtonUp += new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
        }

        /// <summary>
        /// Dettach the specified element where the behavior will start.
        /// </summary>
        protected override void OnDetaching()
        {
            base.OnDetaching();

            // Deregister event handlers
            this.AssociatedObject.MouseLeftButtonDown -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonDown);
            this.AssociatedObject.MouseLeftButtonUp -= new MouseButtonEventHandler(AssociatedObject_MouseLeftButtonUp);
        }

        /// <summary>
        /// Perform the action if it is a hold action.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void AssociatedObject_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if ((DateTime.Now.Subtract(this.clickDownDateTime).TotalMilliseconds) > this.MillisesondsForHoldClick)
            {
                object element = this.TargetObject;

                if (element != null)
                {
                    ManualChangePropertyAction action = new ManualChangePropertyAction(this.AssociatedObject, element, this.PropertyName, this.Value);
                    action.Run();
                }               

                e.Handled = true;
            }
        }

        /// <summary>
        /// Initializes the time when the user has clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void AssociatedObject_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.clickDownDateTime = DateTime.Now;
        }
    }


And this is all. The usage is like the following example:


<Grid Cursor="Hand"
                          Width="75"
                          Margin="0">
                       
                        <i:Interaction.Behaviors>
                            <behaviors:HoldClickBehavior TargetObject="{Binding ElementName=container}"
                                                         PropertyName="Visibility"
                                                         Value="Visible"/>
                        </i:Interaction.Behaviors>
                       
                        <Grid x:Name="container" Visibility="Collapsed">
                           
                        </Grid>
</Grid>

I hope you like this post.

Enjoy!

No hay comentarios:

Publicar un comentario