#756 – Making Element Stop at Edge of Window When Using Inertia

We saw previously how to stop an object at the edge of a window when using touch manipulation.  You likely also want the object to stop moving when it hits the edge of its container when moving as a result of inertia (i.e. you’ve already lifted your finger from the screen).

In the following code fragment, we’ve enabled positional inertia.  We then check to see if the object is past the container boundary.  If it is and if it’s moving as a result of inertia, we stop the inertial event by calling Complete and we move the object back within the window.

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;

            ImageTransform = new MatrixTransform();
        }

        private MatrixTransform imageTransform;
        public MatrixTransform ImageTransform
        {
            get { return imageTransform; }
            set
            {
                if (value != imageTransform)
                {
                    imageTransform = value;
                    RaisePropertyChanged("ImageTransform");
                }
            }
        }

        private void Image_ManipulationStarting(object sender, ManipulationStartingEventArgs e)
        {
            // Ask for manipulations to be reported relative to the canvas
            e.ManipulationContainer = canvMain;

            // Support translation and scaling
            e.Mode = ManipulationModes.Translate;
        }

        private void Image_ManipulationInertiaStarting(object sender, ManipulationInertiaStartingEventArgs e)
        {
            // Translation inertia - 10 in/sec^2 deceleration
            e.TranslationBehavior.DesiredDeceleration = 10.0 * 96.0 / (1000.0 * 1000.0);
        }

        private void Image_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            Matrix m = imageTransform.Matrix;

            // If element beyond edge, report back to WPF
            Vector pastEdgeVector;
            if (ElementPastBoundary(e.Source as FrameworkElement, out pastEdgeVector) &&
                e.IsInertial)
            {
                m.Translate(-1.0 * pastEdgeVector.X, -1.0 * pastEdgeVector.Y);
                imageTransform.Matrix = m;

                e.Complete();
                e.Handled = true;
                return;
            }

            // Find center of element and then transform to get current location of center
            FrameworkElement fe = e.Source as FrameworkElement;
            Point center = new Point(fe.ActualWidth / 2, fe.ActualHeight / 2);
            center = m.Transform(center);

            // Update matrix to reflect translation and rotation
            ManipulationDelta md = e.DeltaManipulation;
            m.Translate(md.Translation.X, md.Translation.Y);

            imageTransform.Matrix = m;
            RaisePropertyChanged("ImageTransform");

            e.Handled = true;
        }

        private bool ElementPastBoundary(FrameworkElement fe, out Vector pastEdgeVector)
        {
            bool pastEdge = false;

            pastEdgeVector = new Vector();

            FrameworkElement feParent = fe.Parent as FrameworkElement;
            if (feParent != null)
            {
                Rect feRect = fe.TransformToAncestor(feParent).TransformBounds(
                    new Rect(0.0, 0.0, fe.ActualWidth, fe.ActualHeight));

                if (feRect.Right > feParent.ActualWidth)
                    pastEdgeVector.X = feRect.Right - feParent.ActualWidth;

                if (feRect.Left < 0)
                    pastEdgeVector.X = feRect.Left;

                if (feRect.Bottom > feParent.ActualHeight)
                    pastEdgeVector.Y = feRect.Bottom - feParent.ActualHeight;

                if (feRect.Top < 0)
                    pastEdgeVector.Y = feRect.Top;

                if ((pastEdgeVector.X != 0) || (pastEdgeVector.Y != 0))
                    pastEdge = true;
            }

            return pastEdge;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

About Sean
Software developer in the Twin Cities area, passionate about .NET technologies. Equally passionate about my own personal projects related to family history and preservation of family stories and photos.

One Response to #756 – Making Element Stop at Edge of Window When Using Inertia

  1. Pingback: Dew Drop – February 15, 2013 (#1,499) | Alvin Ashcraft's Morning Dew

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: