aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Utilities/Tango.Stubs.UI/ViewModels/MainViewVM.cs
blob: 4027af171618dfbc92316dcd92f778774ea74aaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
using Google.Protobuf;
using Google.Protobuf.Collections;
using Microsoft.Win32;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
using Tango.Core.Commands;
using Tango.PMR;
using Tango.Scripting;
using Tango.Settings;
using Tango.SharedUI;
using Tango.Stubs.UI.Views;
using Tango.Stubs.ViewModels;
using Tango.Transport.Adapters;

namespace Tango.Stubs.UI.ViewModels
{
    /// <summary>
    /// Represents the script execution utility main view model.
    /// </summary>
    /// <seealso cref="Tango.SharedUI.ViewModel" />
    public class MainViewVM : ViewModel
    {
        private StubsViewVM _stubsViewVM;
        /// <summary>
        /// Gets or sets the stubs view vm.
        /// </summary>
        public StubsViewVM StubsViewVM
        {
            get { return _stubsViewVM; }
            set { _stubsViewVM = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MainViewVM"/> class.
        /// </summary>
        public MainViewVM()
        {
            LogManager.RegisterLogger(new Logging.FileLogger() { Enabled = true });
            StubsViewVM = new StubsViewVM(ConnectionMode.Internal);

            Application.Current.Exit += Current_Exit;
        }

        private void Current_Exit(object sender, ExitEventArgs e)
        {
            StubsViewVM.SaveSettings();
        }
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using Tango.Core.EventArguments;

public static class FrameworkElementExtensions
{
    #region Mouse Or Touch Events

    /// <summary>
    /// Registers for mouse or touch down.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="relativeTo">The relative to.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrTouchDown(this FrameworkElement element, FrameworkElement relativeTo, EventHandler<MouseOrTouchEventArgs> handler)
    {
        bool mousePressed = false;
        bool touchDown = false;

        MouseButtonEventHandler mouseDownHandler = (x, e) =>
        {
            if (!touchDown)
            {
                mousePressed = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = e.GetPosition
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchDown = false;
            }
        };

        //element.MouseDown += mouseDownHandler;
        element.AddHandler(FrameworkElement.MouseDownEvent, mouseDownHandler);

        EventHandler<TouchEventArgs> touchDownHandler = (x, e) =>
        {
            if (!mousePressed)
            {
                touchDown = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.X, e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = (s) => { return e.GetTouchPoint(s).Position; },
                    TouchDevice = e.TouchDevice,
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchDown = false;
            }
        };

        //element.TouchDown += touchDownHandler;
        element.AddHandler(FrameworkElement.TouchDownEvent, touchDownHandler);

        MouseButtonEventHandler mouseUpHandler = (_, __) =>
        {
            touchDown = false;
            mousePressed = false;
        };

        //element.MouseUp += mouseUpHandler;
        element.AddHandler(FrameworkElement.MouseUpEvent, mouseUpHandler);

        //element.TouchDown += (_, __) =>
        //{

        //};
    }

    /// <summary>
    /// Registers for mouse or touch down.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrTouchDown(this FrameworkElement element, EventHandler<MouseOrTouchEventArgs> handler)
    {
        RegisterForMouseOrTouchDown(element, null, handler);
    }

    /// <summary>
    /// Registers for mouse or touch up.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="relativeTo">The relative to.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrTouchUp(this FrameworkElement element, FrameworkElement relativeTo, EventHandler<MouseOrTouchEventArgs> handler)
    {
        bool mouseReleased = false;
        bool touchReleased = false;

        MouseButtonEventHandler mouseUpHandler = (x, e) =>
        {
            if (!touchReleased)
            {
                mouseReleased = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = e.GetPosition
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchReleased = false;
            }
        };

        //element.MouseUp += mouseUpHandler;
        element.AddHandler(FrameworkElement.MouseUpEvent, mouseUpHandler);

        EventHandler<TouchEventArgs> touchUpHandler = (x, e) =>
        {

            if (!mouseReleased)
            {
                touchReleased = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.X, e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = (s) => { return e.GetTouchPoint(s).Position; },
                    TouchDevice = e.TouchDevice,
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchReleased = false;
            }

        };

        //element.TouchUp += touchUpHandler;
        element.AddHandler(FrameworkElement.TouchUpEvent, touchUpHandler);

        EventHandler<TouchEventArgs> touchDownHandler = (_, __) =>
        {
            touchReleased = false;
            mouseReleased = false;
        };

        //element.TouchDown += touchDownHandler;
        element.AddHandler(FrameworkElement.TouchDownEvent, touchDownHandler);
    }

    /// <summary>
    /// Registers for mouse or touch up.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrTouchUp(this FrameworkElement element, EventHandler<MouseOrTouchEventArgs> handler)
    {
        RegisterForMouseOrTouchUp(element, null, handler);
    }

    /// <summary>
    /// Registers for preview mouse or touch down.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="relativeTo">The relative to.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForPreviewMouseOrTouchDown(this FrameworkElement element, FrameworkElement relativeTo, EventHandler<MouseOrTouchEventArgs> handler)
    {
        bool mousePressed = false;
        bool touchDown = false;

        MouseButtonEventHandler previewMouseDownHandler = (x, e) =>
        {
            if (!touchDown)
            {
                mousePressed = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = e.GetPosition
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchDown = false;
            }
        };

        element.AddHandler(FrameworkElement.PreviewMouseDownEvent, previewMouseDownHandler);
        //element.PreviewMouseDown += previewMouseDownHandler;

        EventHandler<TouchEventArgs> previewTouchDownHandler = (x, e) =>
        {
            if (!mousePressed)
            {
                touchDown = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.X, e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = (s) => { return e.GetTouchPoint(s).Position; },
                    TouchDevice = e.TouchDevice,
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchDown = false;
            }
        };

        //element.PreviewTouchDown += previewTouchDownHandler;
        element.AddHandler(FrameworkElement.PreviewTouchDownEvent, previewTouchDownHandler);

        MouseButtonEventHandler previewMouseUpHandler = (_, __) =>
        {
            touchDown = false;
            mousePressed = false;
        };

        //element.PreviewMouseUp += previewMouseUpHandler;
        element.AddHandler(FrameworkElement.PreviewMouseUpEvent, previewMouseUpHandler);
    }

    /// <summary>
    /// Registers for preview mouse or touch down.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForPreviewMouseOrTouchDown(this FrameworkElement element, EventHandler<MouseOrTouchEventArgs> handler)
    {
        RegisterForPreviewMouseOrTouchDown(element, null, handler);
    }

    /// <summary>
    /// Registers for preview mouse or touch up.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="relativeTo">The relative to.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForPreviewMouseOrTouchUp(this FrameworkElement element, FrameworkElement relativeTo, EventHandler<MouseOrTouchEventArgs> handler)
    {
        bool mouseReleased = false;
        bool touchReleased = false;

        MouseButtonEventHandler previewMouseUpHandler = (x, e) =>
        {
            if (!touchReleased)
            {
                mouseReleased = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = e.GetPosition
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchReleased = false;
            }
        };

        //element.PreviewMouseUp += previewMouseUpHandler;
        element.AddHandler(FrameworkElement.PreviewMouseUpEvent, previewMouseUpHandler);

        EventHandler<TouchEventArgs> previewTouchHandler = (x, e) =>
        {

            if (!mouseReleased)
            {
                touchReleased = true;

                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.X, e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = (s) => { return e.GetTouchPoint(s).Position; },
                    TouchDevice = e.TouchDevice,
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
            else
            {
                touchReleased = false;
            }

        };

        //element.PreviewTouchUp += previewTouchHandler;
        element.AddHandler(FrameworkElement.PreviewTouchUpEvent, previewTouchHandler);

        EventHandler<TouchEventArgs> previewTouchDownHandler = (_, __) =>
        {
            touchReleased = false;
            mouseReleased = false;
        };

        //element.PreviewTouchDown += previewTouchDownHandler;
        element.AddHandler(FrameworkElement.PreviewTouchDownEvent, previewTouchDownHandler);
    }

    /// <summary>
    /// Registers for preview mouse or touch up.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForPreviewMouseOrTouchUp(this FrameworkElement element, EventHandler<MouseOrTouchEventArgs> handler)
    {
        RegisterForPreviewMouseOrTouchUp(element, null, handler);
    }

    /// <summary>
    /// Registers for mouse or touch move.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="relativeTo">The relative to.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrTouchMove(this FrameworkElement element, FrameworkElement relativeTo, EventHandler<MouseOrTouchEventArgs> handler)
    {
        bool _isTouch = false;

        MouseEventHandler mouseHandler = (x, e) =>
        {
            if (!_isTouch)
            {
                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = e.GetPosition
                };

                handler(element, args);
                e.Handled = args.Handled;
            }
        };

        //element.MouseMove += mouseHandler;
        element.AddHandler(FrameworkElement.MouseMoveEvent, mouseHandler);

        EventHandler<TouchEventArgs> touchHandler = (x, e) =>
        {
            _isTouch = true;

            var args = new MouseOrTouchEventArgs()
            {
                Location = new Point(e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.X, e.GetTouchPoint(relativeTo != null ? relativeTo : element).Position.Y),
                Source = e.Source,
                OriginalSource = e.OriginalSource,
                Handled = e.Handled,
                GetPositionAction = (s) => { return e.GetTouchPoint(s).Position; },
                TouchDevice = e.TouchDevice,
            };

            handler(element, args);
            e.Handled = args.Handled;
        };

        //element.TouchMove += touchHandler;
        element.AddHandler(FrameworkElement.TouchMoveEvent, touchHandler);
    }

    /// <summary>
    /// Registers for mouse or touch move.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrTouchMove(this FrameworkElement element, EventHandler<MouseOrTouchEventArgs> handler)
    {
        RegisterForMouseOrTouchMove(element, null, handler);
    }

    /// <summary>
    /// Registers for mouse or stylus move.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="relativeTo">The relative to.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrStylusMove(this FrameworkElement element, FrameworkElement relativeTo, EventHandler<MouseOrTouchEventArgs> handler)
    {
        bool _isTouch = false;

        MouseEventHandler mouseHandler = (x, e) =>
        {
            if (!_isTouch)
            {
                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = e.GetPosition
                };

                handler(element, args);
                e.Handled = args.Handled;

                if (Mouse.LeftButton == MouseButtonState.Pressed)
                {
                    var a = 5;
                }
            }
        };

        //element.MouseMove += mouseHandler;
        element.AddHandler(FrameworkElement.MouseMoveEvent, mouseHandler);

        StylusEventHandler touchHandler = (x, e) =>
        {
            _isTouch = true;

            var args = new MouseOrTouchEventArgs()
            {
                Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                Source = e.Source,
                OriginalSource = e.OriginalSource,
                Handled = e.Handled,
                GetPositionAction = (s) => { return e.GetPosition(s); },
                StylusDevice = e.StylusDevice,
            };

            handler(element, args);
            e.Handled = args.Handled;
        };


        //element.TouchMove += touchHandler;
        element.AddHandler(FrameworkElement.StylusMoveEvent, touchHandler);
    }

    /// <summary>
    /// Registers for mouse or stylus move.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForMouseOrStylusMove(this FrameworkElement element, EventHandler<MouseOrTouchEventArgs> handler)
    {
        RegisterForMouseOrStylusMove(element, null, handler);
    }

    /// <summary>
    /// Registers for mouse or stylus move.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="relativeTo">The relative to.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForPreviewMouseOrStylusMove(this FrameworkElement element, FrameworkElement relativeTo, EventHandler<MouseOrTouchEventArgs> handler)
    {
        bool _isTouch = false;

        MouseEventHandler mouseHandler = (x, e) =>
        {
            if (!_isTouch)
            {
                var args = new MouseOrTouchEventArgs()
                {
                    Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                    Source = e.Source,
                    OriginalSource = e.OriginalSource,
                    Handled = e.Handled,
                    GetPositionAction = e.GetPosition
                };

                handler(element, args);
                e.Handled = args.Handled;

                if (Mouse.LeftButton == MouseButtonState.Pressed)
                {
                    var a = 5;
                }
            }
        };

        //element.MouseMove += mouseHandler;
        element.AddHandler(FrameworkElement.PreviewMouseMoveEvent, mouseHandler);

        StylusEventHandler touchHandler = (x, e) =>
        {
            _isTouch = true;

            var args = new MouseOrTouchEventArgs()
            {
                Location = new Point(e.GetPosition(relativeTo != null ? relativeTo : element).X, e.GetPosition(relativeTo != null ? relativeTo : element).Y),
                Source = e.Source,
                OriginalSource = e.OriginalSource,
                Handled = e.Handled,
                GetPositionAction = (s) => { return e.GetPosition(s); },
                StylusDevice = e.StylusDevice,
            };

            handler(element, args);
            e.Handled = args.Handled;
        };


        //element.TouchMove += touchHandler;
        element.AddHandler(FrameworkElement.StylusMoveEvent, touchHandler);
    }

    /// <summary>
    /// Registers for mouse or stylus move.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForPreviewMouseOrStylusMove(this FrameworkElement element, EventHandler<MouseOrTouchEventArgs> handler)
    {
        RegisterForPreviewMouseOrStylusMove(element, null, handler);
    }

    #endregion

    #region Loaded

    /// <summary>
    /// Registers for the element loaded event. if the element is already loaded will invoke the handler immediately.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="handler">The handler.</param>
    public static void RegisterForLoadedOrNow(this FrameworkElement element, EventHandler handler)
    {
        if (element.IsLoaded)
        {
            handler(element, new EventArgs());
        }
        else
        {
            bool isLoaded = false;

            element.AddHandler(FrameworkElement.LoadedEvent, new RoutedEventHandler((x, y) =>
            {
                if (!isLoaded)
                {
                    isLoaded = true;

                    handler(element, new EventArgs());
                }
            }));
        }
    }

    #endregion

    #region Visual Tree
    /// <summary>
    /// A recursive search for a child of type T in memory and not on logical/visual tree.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="element">The element.</param>
    /// <returns></returns>
    public static T FindChildOffline<T>(this FrameworkElement element) where T : DependencyObject
    {
        if (element == null) return null;

        PropertyInfo contentProp = element.GetType().GetProperty("Child");

        if (contentProp == null)
        {
            contentProp = element.GetType().GetProperty("Content");
        }

        if (contentProp != null)
        {
            var value = contentProp.GetValue(element);

            if (value != null)
            {
                if (value.GetType() == typeof(T))
                {
                    return (T)value;
                }
                else
                {
                    return FindChildOffline<T>(value as FrameworkElement);
                }
            }
        }

        if (element is Panel)
        {
            foreach (var child in (element as Panel).Children.OfType<FrameworkElement>())
            {
                if (child.GetType() == typeof(T))
                {
                    return child as T;
                }
                else
                {
                    var result = FindChildOffline<T>(child);

                    if (result != null)
                    {
                        return result;
                    }
                }
            }
        }

        var contentAtt = element.GetType().GetCustomAttribute<ContentPropertyAttribute>();

        if (contentAtt != null)
        {
            contentProp = element.GetType().GetProperty(contentAtt.Name);

            if (contentProp != null)
            {
                var content = contentProp.GetValue(element);

                if (content != null)
                {
                    if (content is IEnumerable)
                    {
                        foreach (var child in (content as IEnumerable))
                        {
                            if (child.GetType() == typeof(T))
                            {
                                return child as T;
                            }
                            else
                            {
                                var result = FindChildOffline<T>(child as FrameworkElement);

                                if (result != null)
                                {
                                    return result;
                                }
                            }
                        }
                    }
                    else
                    {
                        var result = FindChildOffline<T>(content as FrameworkElement);

                        if (result != null)
                        {
                            return result;
                        }
                    }
                }
            }
        }

        return null;
    }
    #endregion

    #region Bitmaps

    /// <summary>
    /// Creates a bitmap source from the framework element.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <returns></returns>
    public static RenderTargetBitmap TakeSnapshot(this FrameworkElement element)
    {

        int nWidth = (int)Math.Ceiling(element.ActualWidth);
        int nHeight = (int)Math.Ceiling(element.ActualHeight);

        RenderTargetBitmap oTargetBitmap = new RenderTargetBitmap(
            nWidth,
            nHeight,
            96,
            96,
            PixelFormats.Pbgra32
        );

        DrawingVisual oDrawingVisual = new DrawingVisual();

        using (DrawingContext oDrawingContext = oDrawingVisual.RenderOpen())
        {
            VisualBrush oVisualBrush = new VisualBrush(element) { Stretch = Stretch.Fill };

            oDrawingContext.DrawRectangle(
                oVisualBrush,
                null,
                new Rect(
                    new Point(),
                    new Size(nWidth, nHeight)
                )
            );

            oDrawingContext.Close();
            oTargetBitmap.Render(oDrawingVisual);
        }

        return oTargetBitmap;
    }

    #endregion

    #region Animations

    /// <summary>
    /// Starts a double animation on the specified property.
    /// </summary>
    /// <param name="element">The element.</param>
    /// <param name="property">The property.</param>
    /// <param name="duration">The duration.</param>
    /// <param name="acceleration">The acceleration.</param>
    /// <param name="deceleration">The deceleration.</param>
    /// <param name="onComplete">The on complete.</param>
    /// <returns></returns>
    public static DoubleAnimation StartDoubleAnimation(this FrameworkElement element, DependencyProperty property, TimeSpan duration, double to, double? from = null, double? acceleration = null, double? deceleration = null, Action onComplete = null, bool autoReverse = false)
    {
        DoubleAnimation ani = new DoubleAnimation();
        ani.Duration = duration;
        ani.AutoReverse = autoReverse;

        if (acceleration.HasValue)
        {
            ani.AccelerationRatio = acceleration.Value;
        }

        if (deceleration.HasValue)
        {
            ani.DecelerationRatio = deceleration.Value;
        }

        if (ani.From.HasValue)
        {
            ani.From = from.Value;
        }

        ani.To = to;

        ani.Completed += (x, y) => onComplete?.Invoke();
        element.BeginAnimation(property, ani);
        return ani;
    }

    #endregion

    #region Size

    /// <summary>
    /// Ensures the element has height other than NaN. If the element height equals NaN, the method will assign ActualHeight to Height.
    /// </summary>
    /// <param name="element">The element.</param>
    public static void EnsureHeight(this FrameworkElement element)
    {
        if (double.IsNaN(element.Height))
        {
            element.Height = element.ActualHeight;
        }
    }

    #endregion
}