How-To: Extender un control de Silverlight

Una de las ventajas que nos proporcionan los tipos de WPF y Silverlight es la de extender mediante herencia la funcionalidad de una determinada clase del Framework.

Esto quiere decir que podemos partir de un control que ya disponga de la funcionalidad base que necesitamos y extenderla mediante código personalizado para agregarle una determinada funcionalidad que mejore o aumente dicha funcionalidad.

Un ejemplo de los tipos que podemos extender son todos aquellos que herenden de la clase Panel, como por ejemplo un Grid.

En el ejemplo que muestro a continuación veremos como añadirle a un  Grid el comportamiento de pivotar en relación a la posición actual del ratón haciendo uso de la transfomación de proyección de plano de Silverlight 3.0.

Este tipo de proyección en combinación con las animaciones que nos proporciona Silverlight consiguen un efecto de desplazamiento suave en respuesta a la interacción del usuario.

 

using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace PlaneProyect
{
    /// <summary>
    /// Overriden Grid type for integrated Mouse Plane Proyection
    /// </summary>
    public class FloatingGrid: Grid
    {

        #region Private Fields

        /// <summary>
        /// Store the Grid's Plane Proyection 
        /// </summary>
        private PlaneProjection _planeP;

        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor with events subscriptions
        /// </summary>
        public FloatingGrid()
        {
            MouseMove += (FloatingGrid_MouseMove);
            MouseEnter += (FloatingGrid_MouseEnter);
            MouseLeave += (FloatingGrid_MouseLeave);
            Loaded += (FloatingGrid_Loaded);
        }

        #endregion

        #region Properties

        [Category("Common Properties")]
        public double MaximunAngle
        {
            get { return (double)GetValue(MaximunAngleProperty); }
            set { SetValue(MaximunAngleProperty, value); }
        }

        public static readonly DependencyProperty MaximunAngleProperty =
            DependencyProperty.Register("MaximunAngle", typeof(double), typeof(FloatingGrid), new PropertyMetadata(10.0));

        #endregion

        #region Methods

        /// <summary>
        /// Animate Plane Proyection to a determinate position
        /// </summary>
        /// <param name="x">X Angle to move</param>
        /// <param name="y">Y angle to move</param>
        private void AnimateTo(double x, double y)
        {
            var duration = new Duration(TimeSpan.FromSeconds(0.44));

            var xAnimation = new DoubleAnimation();
            var yAnimation = new DoubleAnimation();

            xAnimation.Duration = duration;
            yAnimation.Duration = duration;

            xAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut};
            yAnimation.EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut };

            var sb = new Storyboard {Duration = duration};

            sb.Children.Add(xAnimation);
            sb.Children.Add(yAnimation);

            Storyboard.SetTarget(xAnimation, _planeP);
            Storyboard.SetTarget(yAnimation, _planeP);

            var xPath = new PropertyPath(PlaneProjection.RotationXProperty);
            var yPath = new PropertyPath(PlaneProjection.RotationYProperty);

            Storyboard.SetTargetProperty(xAnimation, xPath);
            Storyboard.SetTargetProperty(yAnimation, yPath);

            xAnimation.To = x;
            yAnimation.To = y;

            sb.Begin();

        }

        /// <summary>
        /// Determine the current angle to animate
        /// </summary>
        /// <param name="e">Gets the current mouse position</param>
        /// <returns>The X and Y Angles</returns>
        private Point GetCurrentAngles(MouseEventArgs e)
        {
            if (double.IsNaN(ActualWidth) || ActualWidth == 0) return new Point(0,0);
            var actualPos = e.GetPosition(this);
            var centerPoint = new Point(ActualWidth / 2, ActualHeight / 2);
            if (MaximunAngle <= 0) MaximunAngle = 10;
            var y = (actualPos.X - centerPoint.X) / centerPoint.X * MaximunAngle;
            var x = (actualPos.Y - centerPoint.Y) / centerPoint.Y * MaximunAngle;
            return new Point(x,y);
        }

        #endregion

        #region Event Handlers

        void FloatingGrid_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateLayout();
            _planeP = new PlaneProjection();
            Projection = _planeP;
        }

        void FloatingGrid_MouseLeave(object sender, MouseEventArgs e)
        {
            AnimateTo(0,0);
        }

        void FloatingGrid_MouseEnter(object sender, MouseEventArgs e)
        {
            var pos = GetCurrentAngles(e);
            AnimateTo(pos.X, pos.Y);
        }

        void FloatingGrid_MouseMove(object sender, MouseEventArgs e)
        {
            var pos = GetCurrentAngles(e);
            AnimateTo(pos.X, pos.Y);
        }

        #endregion
       
    }
}

 

El resultado visual de este control al desplazar el ratón sobre él es similar al mostrado a continuación:

image

 

Un saludo,