Adding simple Multitouch Behaviour to Graph#

May 31, 2012 at 2:40 PM
Edited Jun 4, 2012 at 12:49 PM

Hey guys,

 

i was recently searching for multitouch support for Graph#, but didn't find anything except two or three discussions about it with no solution.

Therefore i examined the code of this framework and i think i found the right point to start.

So here's my solution for those of you, that are also trying to add mt to Graph# :)

Open the following file: GraphSharp.Controls -> Themes -> Generic.xaml

Within this file, there's a style defined, that targets VertexControls 

<Style TargetType="{x:Type Controls:VertexControl}">

and has three special properties defined:

<Setter Property="extbehaviour:DragBehaviour.IsDragEnabled"
                Value="True" />
<Setter Property="extbehaviour:DragBehaviour.X"
                Value="{Binding RelativeSource={RelativeSource Self},Path=(Controls:GraphCanvas.X),Mode=TwoWay}" />
<Setter Property="extbehaviour:DragBehaviour.Y"
                Value="{Binding RelativeSource={RelativeSource Self},Path=(Controls:GraphCanvas.Y),Mode=TwoWay}" />

Those are the properties used for dragging the vertices around.

Now all i have done is writing my own attached behaviour, that listens to touch events instead of mouse events:

 class DragTest
    {


        public static bool GetDraggable(DependencyObject obj)
        {
            return (bool)obj.GetValue(DraggableProperty);
        }

        public static void SetDraggable(DependencyObject obj, bool value)
        {
            obj.SetValue(DraggableProperty, value);
        }

        // Using a DependencyProperty as the backing store for Draggable.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DraggableProperty =
            DependencyProperty.RegisterAttached("Draggable", typeof(bool), typeof(DragTest), new UIPropertyMetadata(false, OnDraggableChanged));


        private static void OnDraggableChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            new SimpleDraghandler(o as FrameworkElement);
        }

        class SimpleDraghandler
        {
            private Dictionary<int, Info> touchInfos = new Dictionary<int,Info>();
           
            public SimpleDraghandler(FrameworkElement frameworkElement)
            {
                frameworkElement.PreviewTouchDown += this.DragSource_PreviewTouchDown;
                frameworkElement.PreviewTouchUp += this.DragSource_PreviewTouchUp;
                frameworkElement.PreviewTouchMove += this.DragSource_PreviewTouchMove;
            }


            public void DragSource_PreviewTouchDown(object sender, System.Windows.Input.TouchEventArgs e)
            {
                FrameworkElement findSource = e.OriginalSource as FrameworkElement;
                VertexControl draggedElement = null;

                while (draggedElement == null && findSource != null)
                {
                    if ((draggedElement = findSource as VertexControl) == null)
                    {
                        findSource = VisualTreeHelper.GetParent(findSource) as FrameworkElement;
                    }
                }

                if (draggedElement == null)
                {
                    return;
                }

                GraphCanvas parentElement = null;
                while (parentElement == null && findSource != null)
                {
                    if ((parentElement = findSource as GraphCanvas) == null)
                    {
                        findSource = VisualTreeHelper.GetParent(findSource) as FrameworkElement;
                    }
                }

                if (parentElement == null)
                {
                    return;
                }
            
                if(!touchInfos.ContainsKey(e.TouchDevice.Id))
                    touchInfos.Add(e.TouchDevice.Id, new Info(draggedElement, parentElement , e.TouchDevice.GetTouchPoint(null).Position));
                IEnumerable<TouchDevice> devices = draggedElement.TouchesCaptured;
                if (!devices.Contains(e.TouchDevice))
                {
                    draggedElement.CaptureTouch(e.TouchDevice);
                }
                e.Handled = true;
            }

            public void DragSource_PreviewTouchUp(object sender, System.Windows.Input.TouchEventArgs e)
            {
                Info info;
                touchInfos.TryGetValue(e.TouchDevice.Id, out info);
                if (info != null)
                {
                    info.DraggedElement.ReleaseTouchCapture(e.TouchDevice);
                    touchInfos.Remove(e.TouchDevice.Id);
                }
                e.Handled = true;
            }

            public void DragSource_PreviewTouchMove(object sender, System.Windows.Input.TouchEventArgs e)
            {

                Info info;
                touchInfos.TryGetValue(e.TouchDevice.Id, out info);
                if (info != null)
                {
                    //    Point dragStart = info.DragStartPosition;
                    Point newPosition = e.GetTouchPoint(null).Position;
                    Point startPosition = info.ParentElement.PointFromScreen(info.DragStartPosition);
                    Point currentPosition = info.ParentElement.PointFromScreen(newPosition);

                        // falls der finger sich weit genug bewegt hat wird ein DND ausgel�st
                    if (Math.Abs(startPosition.X - currentPosition.X) > SystemParameters.MinimumHorizontalDragDistance ||
                            Math.Abs(startPosition.Y - currentPosition.Y) > SystemParameters.MinimumVerticalDragDistance)
                        {
                       
                            info.DragStartPosition = newPosition;
                            //Point currentPos = e.GetTouchPoint(info.ParentElement).Position;
                            Vector displacement = currentPosition - startPosition;
                            double currentX = (double)info.DraggedElement.GetValue(GraphCanvas.XProperty);
                            double currentY = (double)info.DraggedElement.GetValue(GraphCanvas.YProperty);
               
               
                            info.DraggedElement.SetValue(DragTest.DragXProperty, currentX + displacement.X);
                            info.DraggedElement.SetValue(DragTest.DragYProperty, currentY + displacement.Y);

                        }
                }
                e.Handled = true;
            }
        }

        class Info
        {
            public Point DragStartPosition { get; set; }
            public VertexControl DraggedElement { get; set; }
            public GraphCanvas ParentElement { get; set; }

            public Info(VertexControl element, GraphCanvas parentElement, Point dragStartPosition)
            {
                DragStartPosition = dragStartPosition;
                DraggedElement = element;
                ParentElement = parentElement;
            }
        }

        public static double GetDragX(DependencyObject obj)
        {
            return (double)obj.GetValue(DragXProperty);
        }

        public static void SetDragX(DependencyObject obj, double value)
        {
            obj.SetValue(DragXProperty, value);
        }

        // Using a DependencyProperty as the backing store for DragX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DragXProperty =
            DependencyProperty.RegisterAttached("DragX", typeof(double), typeof(DragTest));



        public static double GetDragY(DependencyObject obj)
        {
            return (double)obj.GetValue(DragYProperty);
        }

        public static void SetDragY(DependencyObject obj, double value)
        {
            obj.SetValue(DragYProperty, value);
        }

        // Using a DependencyProperty as the backing store for DragY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DragYProperty =
            DependencyProperty.RegisterAttached("DragY", typeof(double), typeof(DragTest));

    }

Now all i had to do was replacing the above named properties with my own attached property:

 <Setter Property="Sample:DragTest.Draggable" Value="True"/>
 <Setter Property="Sample:DragTest.DragX" Value="{Binding Path=(GraphSharp_Controls:GraphCanvas.X), Mode=TwoWay, RelativeSource={RelativeSource Self}}"/>
 <Setter Property="Sample:DragTest.DragY" Value="{Binding Path=(GraphSharp_Controls:GraphCanvas.Y), Mode=TwoWay, RelativeSource={RelativeSource Self}}"/>

This is my first approach and i know that it is still a little bit buggy, but i hope that it helps those of you who wanted to add mt to Graph# and did'nt know where to start :)

Any suggestions for improvement are welcome :)

With kind regards,

Thomas