/// <summary>
        /// Implementation of <see cref="FilterManipulation"/> that forces the rotation to be about
        /// the center of the manipulation target.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void RotateAboutCenter(object sender, FilterManipulationEventArgs args)
        {
            var inputProcessor = sender as InputProcessor;
            var target = inputProcessor.Target;

            // Get the bounding box of the manipulation target, expressed in the coordinate system of its container
            var rect = target.RenderTransform.TransformBounds(
                new Windows.Foundation.Rect(0, 0, target.ActualWidth, target.ActualHeight));

            args.Pivot = new Windows.Foundation.Point
            {
                X = rect.Left + (rect.Width / 2),
                Y = rect.Top + (rect.Height / 2)
            };
        }
        /// <summary>
        /// Implementation of <see cref="FilterManipulation"/> that forces the rotation to be about
        /// the center of the manipulation target.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void RotateAboutCenter(object sender, FilterManipulationEventArgs args)
        {
            var inputProcessor = sender as InputProcessor;
            var target = inputProcessor.Target;

            // Get the bounding box of the manipulation target, expressed in the coordinate system of its container
            var rect = target.RenderTransform.TransformBounds(
                 new Windows.Foundation.Rect(0, 0, target.ActualWidth, target.ActualHeight));

            args.Pivot = new Windows.Foundation.Point
            {
                X = rect.Left + (rect.Width / 2),
                Y = rect.Top + (rect.Height / 2)
            };
        }
        /// <summary>
        /// Implementation of <see cref="FilterManipulation"/> that forces the center of mass of the
        /// manipulation target to remain inside its container.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void ClampCenterOfMass(object sender, FilterManipulationEventArgs args)
        {
            var inputProcessor = sender as InputProcessor;
            var target = inputProcessor.Target;
            var container = inputProcessor.Reference;

            // Get the bounding box and the center of mass of the manipulation target,
            // expressed in the coordinate system of its container
            var rect = target.RenderTransform.TransformBounds(
                 new Windows.Foundation.Rect(0, 0, target.ActualWidth, target.ActualHeight));

            var centerOfMass = new Windows.Foundation.Point
            {
                X = rect.Left + (rect.Width / 2),
                Y = rect.Top + (rect.Height / 2)
            };

            // Apply delta transform to the center of mass
            var transform = new Windows.UI.Xaml.Media.CompositeTransform
            {
                CenterX = args.Pivot.X,
                CenterY = args.Pivot.Y,
                Rotation = args.Delta.Rotation,
                ScaleX = args.Delta.Scale,
                ScaleY = args.Delta.Scale,
                TranslateX = args.Delta.Translation.X,
                TranslateY = args.Delta.Translation.Y
            };

            var transformedCenterOfMass = transform.TransformPoint(centerOfMass);

            // Reset the transformation if the transformed center of mass falls outside the container
            if (transformedCenterOfMass.X < 0 || transformedCenterOfMass.X > container.ActualWidth ||
                 transformedCenterOfMass.Y < 0 || transformedCenterOfMass.Y > container.ActualHeight)
            {
                args.Delta = new Windows.UI.Input.ManipulationDelta
                {
                    Rotation = 0F,
                    Scale = 1F,
                    Translation = new Windows.Foundation.Point(0, 0)
                };
            }
        }
        /// <summary>
        /// Implementation of <see cref="FilterManipulation"/> that forces at least <see cref="ManipulationFilter.TargetMinSize"/>
        /// pixels of the manipulation target to remain inside its container.
        /// This filter also makes sure the manipulation target does not become too small or too big.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void Clamp(object sender, FilterManipulationEventArgs args)
        {
            var inputProcessor = sender as InputProcessor;
            var target = inputProcessor.Target;
            var container = inputProcessor.Reference;

            //int x = 0;
            //int.TryParse(target.GetValue(Windows.UI.Xaml.Controls.Canvas.TopProperty).ToString(), out x);

            //int y = 0;
            //int.TryParse(target.GetValue(Windows.UI.Xaml.Controls.Canvas.LeftProperty).ToString(), out y);

            // Get the bounding box of the manipulation target, expressed in the coordinate system of its container
            var rect = target.RenderTransform.TransformBounds(new Windows.Foundation.Rect(0, 0, target.ActualWidth, target.ActualHeight));

            // Make sure the manipulation target does not go completely outside the boundaries of its container
            var translate = new Windows.Foundation.Point
            {
                X = args.Delta.Translation.X,
                Y = args.Delta.Translation.Y
            };
            if ((args.Delta.Translation.X > 0 && args.Delta.Translation.X > container.ActualWidth - rect.Left - ManipulationFilter.TargetMinInside) ||
                 (args.Delta.Translation.X < 0 && args.Delta.Translation.X < ManipulationFilter.TargetMinInside - rect.Right) ||
                 (args.Delta.Translation.Y > 0 && args.Delta.Translation.Y > container.ActualHeight - rect.Top - ManipulationFilter.TargetMinInside) ||
                 (args.Delta.Translation.Y < 0 && args.Delta.Translation.Y < ManipulationFilter.TargetMinInside - rect.Bottom))
            {
                translate.X = 0;
                translate.Y = 0;
            }

            // Make sure the manipulation target does not become too small, or too big
            float scale = args.Delta.Scale < 1F ?
                 (float)System.Math.Max(ManipulationFilter.TargetMinSize / System.Math.Min(rect.Width, rect.Height), args.Delta.Scale) :
                 (float)System.Math.Min(ManipulationFilter.TargetMaxSize / System.Math.Max(rect.Width, rect.Height), args.Delta.Scale);

            args.Delta = new Windows.UI.Input.ManipulationDelta
            {
                Expansion = args.Delta.Expansion,
                Rotation = args.Delta.Rotation,
                Scale = scale,
                Translation = translate
            };
        }
        /// <summary>
        /// Implementation of <see cref="FilterManipulation"/> that forces at least <see cref="ManipulationFilter.TargetMinSize"/>
        /// pixels of the manipulation target to remain inside its container.
        /// This filter also makes sure the manipulation target does not become too small or too big.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void Clamp(object sender, FilterManipulationEventArgs args)
        {
            var inputProcessor = sender as InputProcessor;
            var target = inputProcessor.Target;
            var container = inputProcessor.Reference;

            // Get the bounding box of the manipulation target, expressed in the coordinate system of its container
            var rect = target.RenderTransform.TransformBounds(
                new Windows.Foundation.Rect(0, 0, target.ActualWidth, target.ActualHeight));

            // Make sure the manipulation target does not go completely outside the boundaries of its container
            var translate = new Windows.Foundation.Point
            {
                X = args.Delta.Translation.X,
                Y = args.Delta.Translation.Y
            };
            if ((args.Delta.Translation.X > 0 && args.Delta.Translation.X > container.ActualWidth - rect.Left - ManipulationFilter.TargetMinInside) ||
                (args.Delta.Translation.X < 0 && args.Delta.Translation.X < ManipulationFilter.TargetMinInside - rect.Right) ||
                (args.Delta.Translation.Y > 0 && args.Delta.Translation.Y > container.ActualHeight - rect.Top - ManipulationFilter.TargetMinInside) ||
                (args.Delta.Translation.Y < 0 && args.Delta.Translation.Y < ManipulationFilter.TargetMinInside - rect.Bottom))
            {
                translate.X = 0;
                translate.Y = 0;
            }

            // Make sure the manipulation target does not become too small, or too big
            float scale = args.Delta.Scale < 1F ?
                (float)System.Math.Max(ManipulationFilter.TargetMinSize / System.Math.Min(rect.Width, rect.Height), args.Delta.Scale) :
                (float)System.Math.Min(ManipulationFilter.TargetMaxSize / System.Math.Max(rect.Width, rect.Height), args.Delta.Scale);

            args.Delta = new Windows.UI.Input.ManipulationDelta
            {
                Expansion = args.Delta.Expansion,
                Rotation = args.Delta.Rotation,
                Scale = scale,
                Translation = translate
            };
        }
        private void OnManipulationUpdated(Windows.UI.Input.GestureRecognizer sender, Windows.UI.Input.ManipulationUpdatedEventArgs args)
        {
            _deltaTransform.TranslateX = args.Delta.Translation.X;
            _deltaTransform.TranslateY = args.Delta.Translation.Y;

            _target.SetValue(Windows.UI.Xaml.Controls.Canvas.TopProperty, 0);
            _target.SetValue(Windows.UI.Xaml.Controls.Canvas.LeftProperty, 0);


            // Because of the way we process pointer events, all coordinates are expressed
            // in the coordinate system of the reference of the manipulation target.
            // args.Position stores the position of the pivot of this manipulation
            // args.Delta stores the deltas (Translation, Rotation in degrees, and Scale)

            var filteredArgs = new FilterManipulationEventArgs(args);

            OnFilterManipulation?.Invoke(this, filteredArgs);

            // Update the transform
            // filteredArgs.Pivot indicates the position of the pivot of this manipulation
            // filteredArgs.Delta indicates the deltas (Translation, Rotation in degrees, and Scale)
            _previousTransform.Matrix  = _transform.Value;
            _deltaTransform.CenterX    = filteredArgs.Pivot.X;
            _deltaTransform.CenterY    = filteredArgs.Pivot.Y;
            _deltaTransform.Rotation   = filteredArgs.Delta.Rotation;
            _deltaTransform.TranslateX = filteredArgs.Delta.Translation.X;
            _deltaTransform.TranslateY = filteredArgs.Delta.Translation.Y;

            if (ZoomBehavior == Controls.ZoomType.Resize)
            {
                _target.Height = _target.ActualHeight * filteredArgs.Delta.Scale;
                _target.Width  = _target.ActualWidth * filteredArgs.Delta.Scale;
            }
            else if (ZoomBehavior == Controls.ZoomType.Scale)
            {
                _deltaTransform.ScaleX = _deltaTransform.ScaleY = filteredArgs.Delta.Scale;
            }
        }
        /// <summary>
        /// Implementation of <see cref="FilterManipulation"/> that forces the center of mass of the
        /// manipulation target to remain inside its container.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        public static void ClampCenterOfMass(object sender, FilterManipulationEventArgs args)
        {
            var inputProcessor = sender as InputProcessor;
            var target = inputProcessor.Target;
            var container = inputProcessor.Reference;

            // Get the bounding box and the center of mass of the manipulation target, 
            // expressed in the coordinate system of its container
            var rect = target.RenderTransform.TransformBounds(
                new Windows.Foundation.Rect(0, 0, target.ActualWidth, target.ActualHeight));

            var centerOfMass = new Windows.Foundation.Point
            {
                X = rect.Left + (rect.Width / 2),
                Y = rect.Top + (rect.Height / 2)
            };

            // Apply delta transform to the center of mass
            var transform = new Windows.UI.Xaml.Media.CompositeTransform
            {
                CenterX = args.Pivot.X,
                CenterY = args.Pivot.Y,
                Rotation = args.Delta.Rotation,
                ScaleX = args.Delta.Scale,
                ScaleY = args.Delta.Scale,
                TranslateX = args.Delta.Translation.X,
                TranslateY = args.Delta.Translation.Y
            };

            var transformedCenterOfMass = transform.TransformPoint(centerOfMass);

            // Reset the transformation if the transformed center of mass falls outside the container
            if (transformedCenterOfMass.X < 0 || transformedCenterOfMass.X > container.ActualWidth ||
                transformedCenterOfMass.Y < 0 || transformedCenterOfMass.Y > container.ActualHeight)
            {
                args.Delta = new Windows.UI.Input.ManipulationDelta
                {
                    Rotation = 0F,
                    Scale = 1F,
                    Translation = new Windows.Foundation.Point(0, 0)
                };
            }
        }
        private void OnManipulationUpdated(Windows.UI.Input.GestureRecognizer sender, Windows.UI.Input.ManipulationUpdatedEventArgs args)
        {
            // Because of the way we process pointer events, all coordinates are expressed
            // in the coordinate system of the reference of the manipulation target.
            // args.Position stores the position of the pivot of this manipulation
            // args.Delta stores the deltas (Translation, Rotation in degrees, and Scale)

            var filteredArgs = new FilterManipulationEventArgs(args);
            if (OnFilterManipulation != null)
            {
                OnFilterManipulation(this, filteredArgs);
            }

            // Update the transform
            // filteredArgs.Pivot indicates the position of the pivot of this manipulation
            // filteredArgs.Delta indicates the deltas (Translation, Rotation in degrees, and Scale)
            _previousTransform.Matrix = _transform.Value;
            _deltaTransform.CenterX = filteredArgs.Pivot.X;
            _deltaTransform.CenterY = filteredArgs.Pivot.Y;
            _deltaTransform.Rotation = filteredArgs.Delta.Rotation;
            _deltaTransform.ScaleX = _deltaTransform.ScaleY = filteredArgs.Delta.Scale;
            _deltaTransform.TranslateX = filteredArgs.Delta.Translation.X;
            _deltaTransform.TranslateY = filteredArgs.Delta.Translation.Y;
        }