/// <summary>
 /// Initializes a new instance of the <see cref="Touch"/> class.
 /// </summary>
 /// <param name="view">The view which owns this tracker.</param>
 /// <param name="device">The touch device which is being tracked.</param>
 public Touch(PresentationFoundationView view, TouchDevice device)
     : base(view)
 {
     this.device = device;
 }
Esempio n. 2
0
 /// <inheritdoc/>
 protected override void OnFingerDown(TouchDevice device, Int64 fingerID, Double x, Double y, Single pressure, ref RoutedEventData data)
 {
     HighlightOpacity = 1.0;
     base.OnFingerDown(device, fingerID, x, y, pressure, ref data);
 }
 public bool Subsequent(TouchDevice point)
 {
     return(InputTracker.Subsequent(point));
 }
Esempio n. 4
0
        void TouchMovementStarting(object sender, TouchEventArgs e)
        {
            TouchDevice c = e.TouchDevice;

            initialpoint = c.GetTouchPoint(this).Position;
        }
Esempio n. 5
0
        public X11Window(AvaloniaX11Platform platform, IWindowImpl popupParent)
        {
            _platform = platform;
            _popup    = popupParent != null;
            _x11      = platform.Info;
            _mouse    = new MouseDevice();
            _touch    = new TouchDevice();
            _keyboard = platform.KeyboardDevice;

            var glfeature             = AvaloniaLocator.Current.GetService <IWindowingPlatformGlFeature>();
            XSetWindowAttributes attr = new XSetWindowAttributes();
            var valueMask             = default(SetWindowValuemask);

            attr.backing_store = 1;
            attr.bit_gravity   = Gravity.NorthWestGravity;
            attr.win_gravity   = Gravity.NorthWestGravity;
            valueMask         |= SetWindowValuemask.BackPixel | SetWindowValuemask.BorderPixel
                                 | SetWindowValuemask.BackPixmap | SetWindowValuemask.BackingStore
                                 | SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity;

            if (_popup)
            {
                attr.override_redirect = true;
                valueMask |= SetWindowValuemask.OverrideRedirect;
            }

            XVisualInfo?visualInfo = null;

            // OpenGL seems to be do weird things to it's current window which breaks resize sometimes
            _useRenderWindow = glfeature != null;

            var glx = glfeature as GlxGlPlatformFeature;

            if (glx != null)
            {
                visualInfo = *glx.Display.VisualInfo;
            }
            else if (glfeature == null)
            {
                visualInfo = _x11.TransparentVisualInfo;
            }

            var egl = glfeature as EglGlPlatformFeature;

            var visual = IntPtr.Zero;
            var depth  = 24;

            if (visualInfo != null)
            {
                visual        = visualInfo.Value.visual;
                depth         = (int)visualInfo.Value.depth;
                attr.colormap = XCreateColormap(_x11.Display, _x11.RootWindow, visualInfo.Value.visual, 0);
                valueMask    |= SetWindowValuemask.ColorMap;
            }

            int defaultWidth = 0, defaultHeight = 0;

            if (!_popup && Screen != null)
            {
                var monitor = Screen.AllScreens.OrderBy(x => x.PixelDensity)
                              .FirstOrDefault(m => m.Bounds.Contains(Position));

                if (monitor != null)
                {
                    // Emulate Window 7+'s default window size behavior.
                    defaultWidth  = (int)(monitor.WorkingArea.Width * 0.75d);
                    defaultHeight = (int)(monitor.WorkingArea.Height * 0.7d);
                }
            }

            // check if the calculated size is zero then compensate to hardcoded resolution
            defaultWidth  = Math.Max(defaultWidth, 300);
            defaultHeight = Math.Max(defaultHeight, 200);

            _handle = XCreateWindow(_x11.Display, _x11.RootWindow, 10, 10, defaultWidth, defaultHeight, 0,
                                    depth,
                                    (int)CreateWindowArgs.InputOutput,
                                    visual,
                                    new UIntPtr((uint)valueMask), ref attr);

            if (_useRenderWindow)
            {
                _renderHandle = XCreateWindow(_x11.Display, _handle, 0, 0, defaultWidth, defaultHeight, 0, depth,
                                              (int)CreateWindowArgs.InputOutput,
                                              visual,
                                              new UIntPtr((uint)(SetWindowValuemask.BorderPixel | SetWindowValuemask.BitGravity |
                                                                 SetWindowValuemask.WinGravity | SetWindowValuemask.BackingStore)), ref attr);
            }
            else
            {
                _renderHandle = _handle;
            }

            Handle    = new PlatformHandle(_handle, "XID");
            _realSize = new PixelSize(defaultWidth, defaultHeight);
            platform.Windows[_handle] = OnEvent;
            XEventMask ignoredMask = XEventMask.SubstructureRedirectMask
                                     | XEventMask.ResizeRedirectMask
                                     | XEventMask.PointerMotionHintMask;

            if (platform.XI2 != null)
            {
                ignoredMask |= platform.XI2.AddWindow(_handle, this);
            }
            var mask = new IntPtr(0xffffff ^ (int)ignoredMask);

            XSelectInput(_x11.Display, _handle, mask);
            var protocols = new[]
            {
                _x11.Atoms.WM_DELETE_WINDOW
            };

            XSetWMProtocols(_x11.Display, _handle, protocols, protocols.Length);
            XChangeProperty(_x11.Display, _handle, _x11.Atoms._NET_WM_WINDOW_TYPE, _x11.Atoms.XA_ATOM,
                            32, PropertyMode.Replace, new[] { _x11.Atoms._NET_WM_WINDOW_TYPE_NORMAL }, 1);

            if (platform.Options.WmClass != null)
            {
                SetWmClass(platform.Options.WmClass);
            }

            var surfaces = new List <object>
            {
                new X11FramebufferSurface(_x11.DeferredDisplay, _renderHandle,
                                          depth, () => Scaling)
            };

            if (egl != null)
            {
                surfaces.Insert(0,
                                new EglGlPlatformSurface((EglDisplay)egl.Display, egl.DeferredContext,
                                                         new SurfaceInfo(this, _x11.DeferredDisplay, _handle, _renderHandle)));
            }
            if (glx != null)
            {
                surfaces.Insert(0, new GlxGlPlatformSurface(glx.Display, glx.DeferredContext,
                                                            new SurfaceInfo(this, _x11.Display, _handle, _renderHandle)));
            }

            Surfaces = surfaces.ToArray();
            UpdateMotifHints();
            UpdateSizeHints(null);
            _xic = XCreateIC(_x11.Xim, XNames.XNInputStyle, XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing,
                             XNames.XNClientWindow, _handle, IntPtr.Zero);
            XFlush(_x11.Display);
            if (_popup)
            {
                PopupPositioner = new ManagedPopupPositioner(new ManagedPopupPositionerPopupImplHelper(popupParent, MoveResize));
            }
            if (platform.Options.UseDBusMenu)
            {
                NativeMenuExporter = DBusMenuExporter.TryCreate(_handle);
            }
        }
 public bool Initial(TouchDevice point)
 {
     return(InputTracker.Initial(point));
 }
        //Used to update the length visualisation and displys the bounding circle (using length as the radius)
        private void updateViews(TouchDevice callee)
        {
            // just want to know how WPF handles multi touches and events simulatenously(Observation: This states it is executed by Main: that means other threads just add the event to Main thread's queue :) )
            //Console.WriteLine("Calling Thread: " + Thread.CurrentThread + " Is background Thread? : " + Thread.CurrentThread.IsBackground);
            //update the booleans if there are no blobs/tags
            if (noOfBlobs == 0)
            {
                blobDetected = false;
            }
            else
            {
                blobDetected = true;
            }
            if (noOfTags == 0)
            {
                tagDetected = false;
            }
            else
            {
                tagDetected = true;
            }

            Double distance = 0;

            //Drag the length displayer closer to the calling canvas's point
            lengthDisplayer.SetValue(Canvas.LeftProperty, (Double)callee.GetCenterPosition(this).X + 200);
            lengthDisplayer.SetValue(Canvas.TopProperty, (Double)callee.GetCenterPosition(this).Y + 10);

            //update length
            if (blobDetected)
            {
                TouchDevice blob = blobTouchDevices.FirstOrDefault();
                //Console.WriteLine( "Blob List SIze" + blobTouchDevices.Count + "  TAG List Size : " + tagTouchDevices.Count);
                if (noOfBlobs == 1 && noOfTags == 1)
                {
                    distance             = getDistance(blobTouchDevices[0].GetCenterPosition(this), tagTouchDevices[0].GetCenterPosition(this));
                    lengthDisplayer.Text = "Blob to Tag Distance: " + distance;
                    if (!blob.Equals(callee))
                    {
                        displayBoundingEllipse(distance, blob.GetCenterPosition(this));
                    }
                }
                else if (noOfBlobs == 2)
                {
                    distance             = getDistance(blobTouchDevices[0].GetCenterPosition(this), blobTouchDevices[1].GetCenterPosition(this));
                    lengthDisplayer.Text = "Blobs Distance: " + distance;
                    displayBoundingEllipse(distance, blobTouchDevices[0].GetCenterPosition(this));
                }
            }
            else if (tagDetected)
            {//no blobs detected
                if (noOfTags == 2)
                {
                    distance             = getDistance(tagTouchDevices[0].GetCenterPosition(this), tagTouchDevices[1].GetCenterPosition(this));
                    lengthDisplayer.Text = "Tags Distance: " + distance;

                    TouchDevice bigTag;
                    //We are not interested in the moving part's tag. As we use the bigger tag for updating center of the bounding circle
                    if (tagTouchDevices[0] != callee)
                    {
                        bigTag = tagTouchDevices[0];
                    }
                    else
                    {
                        bigTag = tagTouchDevices[1];
                    }
                    displayBoundingEllipse(distance, bigTag.GetCenterPosition(this));
                }
            }
            else
            {
                lengthDisplayer.Text = "Input not supported.\n OR \nToo few/many inputs. ";
            }
        }
Esempio n. 8
0
 /// <summary>
 ///     Releases capture on any touch devices captured to this element.
 /// </summary>
 public void ReleaseAllTouchCaptures()
 {
     TouchDevice.ReleaseAllCaptures(this);
 }
Esempio n. 9
0
        public void Click_Counting_Should_Work_Correctly_With_Few_Touch_Contacts()
        {
            using var app = UnitTestApp(new TimeSpan(200));

            var root = new TestRoot();
            var touchDevice = new TouchDevice();

            var pointerPressedExecutedTimes = 0;
            var tappedExecutedTimes = 0;
            var isDoubleTapped = false;
            var doubleTappedExecutedTimes = 0;
            root.PointerPressed += (a, e) =>
            {
                pointerPressedExecutedTimes++;
                switch (pointerPressedExecutedTimes)
                {
                    case <= 2:
                        Assert.True(e.ClickCount == 1);
                        break;
                    case 3:
                        Assert.True(e.ClickCount == 2);
                        break;
                    case 4:
                        Assert.True(e.ClickCount == 3);
                        break;
                    case 5:
                        Assert.True(e.ClickCount == 4);
                        break;
                    case 6:
                        Assert.True(e.ClickCount == 5);
                        break;
                    case 7:
                        Assert.True(e.ClickCount == 1);
                        break;
                    case 8:
                        Assert.True(e.ClickCount == 1);
                        break;
                    case 9:
                        Assert.True(e.ClickCount == 2);
                        break;
                    default:
                        break;
                }
            };
            root.DoubleTapped += (a, e) =>
            {
                isDoubleTapped = true;
                doubleTappedExecutedTimes++;
            };
            root.Tapped += (a, e) =>
            {
                tappedExecutedTimes++;
            };
            SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 0, 1);
            SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 0, 1);
            TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 2);
            TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 3);
            TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 4);
            SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchBegin, 5, 6, 7);
            SendXTouchContactsWithIds(InputManager.Instance, touchDevice, root, RawPointerEventType.TouchEnd, 5, 6, 7);
            TapOnce(InputManager.Instance, touchDevice, root, touchPointId: 8);
            Assert.Equal(6, tappedExecutedTimes);
            Assert.Equal(9, pointerPressedExecutedTimes);
            Assert.True(isDoubleTapped);
            Assert.Equal(3, doubleTappedExecutedTimes);
        }
        public static void CaptureTouchDeviceToManipulationEnabledParent(DependencyObject element, TouchDevice touchDevice)
        {
            if (element == null)
            {
                return;
            }

            UIElement parent = VisualTreeHelper.GetParent(element) as UIElement;

            if (parent == null)
            {
                touchDevice.Capture(null);
                return;
            }

            if (parent.IsManipulationEnabled)
            {
                touchDevice.Capture(parent as IInputElement);
                return;
            }

            CaptureTouchDeviceToManipulationEnabledParent(parent, touchDevice);
        }
Esempio n. 11
0
 public TouchDeviceWrapper(TouchDevice device)
 {
     _device = device;
 }
Esempio n. 12
0
        public void InpDeviceDown(Point pos, TouchDevice touchDev)
        {
            if (ShapeHitTester.IsPaletteHit(_doc, pos))
            {
                _palette.StartManip(pos, touchDev);
                return;
            }

            DocTools.UnfocusAll(_doc.GetShapes().Where(sh => !sh.IsManipulated()));
            switch (_modeMgr.Mode)
            {
            case ShapeInputMode.CreationExpected:
                _modeMgr.Mode = ShapeInputMode.ManipulationExpected;
                CreateManipulate(_palette.shapeType, pos.X, pos.Y);
                if (_palette != null)
                {
                    _palette.ResetOvers();
                }
                break;

            case ShapeInputMode.LinkedObj1Expected:
                GetLinkables(pos, touchDev);
                if (linkCreation.end1 != null)
                {
                    ModeMgr.Mode = ShapeInputMode.LinkedObj2Expected;
                }
                break;

            case ShapeInputMode.LinkedObj2Expected:
                GetLinkables(pos, touchDev);
                if (linkCreation.end1 != null && linkCreation.end2 != null)
                {
                    _doc.BeginCreateLink(linkCreation.end1.GetId(), linkCreation.end2.GetId(), linkCreation.linkHead);
                    linkCreation.end1 = null;
                    linkCreation.end2 = null;
                    ModeMgr.Mode      = ShapeInputMode.ManipulationExpected;

                    if (_palette != null)
                    {
                        _palette.ResetOvers();
                    }
                }
                break;

            case ShapeInputMode.ManipulationExpected:
                //no current touch points on shapes (maybe touch points over empty space)

                Shape resizeNode   = null;
                var   underContact = DocTools.DetectSelectedShape(_doc, pos, touchDev, out resizeNode) as IVdShape;
                if (underContact == null)
                {
                    return;
                }

                var shapeFree       = underContact.GetCursor() == null;
                var shapeLockedByUs = false;
                if (!shapeFree)
                {
                    shapeLockedByUs = underContact.GetCursor().OwnerId == _palette.GetOwnerId();
                }

                //this shape free and we don't have cursors
                if (shapeFree && _doc.VolatileCtx.LocalCursor == null)
                {
                    //shape free, try lock it and schedule cursor approval continuation
                    {
                        cursorApproval.resizeNode = resizeNode;
                        cursorApproval.pos        = pos;
                        cursorApproval.td         = touchDev;
                        _modeMgr.Mode             = ShapeInputMode.CursorApprovalExpected;
                    }

                    //take new local cursor
                    _doc.VolatileCtx.BeginTakeShapeWithLocalCursor(underContact.Id());
                }
                else if (shapeLockedByUs)
                {
                    CaptureAndStartManip(underContact, pos, resizeNode, touchDev);
                }
                break;

            case ShapeInputMode.Manipulating:
                break;

            case ShapeInputMode.CursorApprovalExpected:
                break;

            default:
                throw new NotSupportedException();
            }
        }
Esempio n. 13
0
 public GestureCompletedEventArgs(UIElement source, TouchDevice touchDevice)
 {
     Source      = source;
     TouchDevice = touchDevice;
 }
Esempio n. 14
0
 /// <inheritdoc/>
 protected override void OnFingerUp(TouchDevice device, Int64 fingerID, Double x, Double y, Single pressure, ref RoutedEventData data)
 {
     HighlightOpacity = (HighlightOnSelect && IsSelected) || (HighlightOnMouseOver && IsMouseDirectlyOver) ? 1.0 : 0.0;
     base.OnFingerUp(device, fingerID, x, y, pressure, ref data);
 }
Esempio n. 15
0
 public override int GetHashCode()
 {
     return(TouchDevice.GetHashCode() + TouchAction.GetHashCode());
 }
Esempio n. 16
0
        /// <inheritdoc/>
        protected override void OnLostTouchCapture(TouchDevice device, Int64 id, RoutedEventData data)
        {
            HandleCursorUp(id);

            base.OnLostTouchCapture(device, id, data);
        }
Esempio n. 17
0
        /// <inheritdoc/>
        protected override void OnFingerMotion(TouchDevice device, Int64 fingerID, Double x, Double y, Double dx, Double dy, Single pressure, ref RoutedEventData data)
        {
            data.Handled = true;

            base.OnFingerMotion(device, fingerID, x, y, dx, dy, pressure, ref data);
        }
Esempio n. 18
0
        public bool InpDeviceDown(Point pos, TouchDevice touchDev)
        {
            DocTools.UnfocusAll(_doc.GetShapes().Where(sh => !sh.IsManipulated()));
            switch (_modeMgr.Mode)
            {
            case ShapeInputMode.CreationExpected:
                _modeMgr.Mode = ShapeInputMode.ManipulationExpected;
                CreateManipulate(_palette.shapeType, pos.X, pos.Y);
                if (_palette != null)
                {
                    _palette.ResetOvers();
                }
                return(true);

            case ShapeInputMode.LinkedObj1Expected:
                GetLinkables(pos, touchDev);
                if (_linkCreation.end1 != null)
                {
                    ModeMgr.Mode = ShapeInputMode.LinkedObj2Expected;
                }
                return(true);

            case ShapeInputMode.LinkedObj2Expected:
                GetLinkables(pos, touchDev);
                if (_linkCreation.end1 != null && _linkCreation.end2 != null)
                {
                    _linkCreation.linkId = _doc.BeginCreateLink(_linkCreation.end1.GetId(), _linkCreation.end2.GetId(),
                                                                _linkCreation.headType);
                    _linkCreation.end1 = null;
                    _linkCreation.end2 = null;
                    ModeMgr.Mode       = ShapeInputMode.ManipulationExpected;

                    if (_palette != null)
                    {
                        _palette.ResetOvers();
                    }
                }
                return(true);

            case ShapeInputMode.ManipulationExpected:
                //no current touch points on shapes (maybe touch points over empty space)

                Shape resizeNode   = null;
                var   underContact = DocTools.DetectSelectedShape(_doc, pos, touchDev, out resizeNode) as IVdShape;
                if (underContact == null)
                {
                    return(false);
                }

                LockIfPossible(underContact, resizeNode, pos, touchDev);
                return(true);

            case ShapeInputMode.Manipulating:
                DocTools.UnfocusAll(_doc.GetShapes().Where(sh => !sh.IsManipulated()));
                StopManipulation(_doc.VolatileCtx.LocalCursor, true);
                return(true);

            case ShapeInputMode.CursorApprovalExpected:
                return(true);

            default:
                throw new NotSupportedException();
            }
        }
Esempio n. 19
0
 public GestureEventArgs(RoutedEvent routedEvent, TouchDevice device)
     : base(routedEvent)
 {
     _device = device;
 }
 public bool Contains(TouchDevice point)
 {
     return(InputTracker.Contains(point));
 }
Esempio n. 21
0
 public GestureEventArgs(RoutedEvent routedEvent, object source, TouchDevice device)
     : base(routedEvent, source)
 {
     _device = device;
 }
 public bool Intermediate(TouchDevice point)
 {
     return(InputTracker.Intermediate(point));
 }
Esempio n. 23
0
        private bool CheckIsAreaGesture(ManipulationDeltaEventArgs e)
        {
            int fingerCount = this.AssociatedObject.TouchesOver.Count();

            if (fingerCount > this.gestureFingerCount)
            {
                this.gestureFingerCount = fingerCount;
            }

            TouchDevice device       = (TouchDevice)e.Manipulators.First();
            TouchPoint  point        = device.GetTouchPoint(Application.Current.MainWindow);
            double      mendedHeight = GestureDetector.GetMendedHeight(point.Size.Width, point.Size.Height);

            if (GestureDetector.IsErase(point.Size.Width, mendedHeight))
            {
                if (this.OnGestureDetector != null)
                {
                    this.currentResult = GestureResult.Brush;
                    GestureArg arg = new GestureArg(this.AssociatedObject, this.currentResult, device);
                    arg.Tag = new Rect(point.Position.X, point.Position.Y, point.Size.Width, mendedHeight);
                    this.OnGestureDetector(this.AssociatedObject, arg);
                }
                this.isGestureBehavior = true;
                return(true);
            }
            else
            {
                if (this.gestureFingerCount == 1)
                {
                    double checkWidht  = point.Size.Width;
                    double checkHeight = mendedHeight;
                    double checkRadio  = checkHeight / checkWidht;
                    bool   isZoom      = this.IsZoom(e.CumulativeManipulation.Scale);
                    if (
                        (checkRadio > GestureConsts.Current.FingerHeightWidhtRatio || checkWidht < (1 / GestureConsts.Current.FingerHeightWidhtRatio)) &&
                        (Math.Min(checkWidht, checkHeight) > GestureConsts.Current.OneFingerSize)
                        )
                    {
                        if (this.gestureStatusBehavior != null)
                        {
                            this.gestureStatusBehavior(new GestureArg(this.AssociatedObject, GestureResult.MultipleFingers, device));
                        }
                        if (e.CumulativeManipulation.Translation.Length > GestureConsts.Current.MultiFingerDragThreshold && isZoom == false)
                        {
                            if (this.OnGestureDetector != null)
                            {
                                this.currentResult = GestureResult.Drag;
                                GestureArg arg = new GestureArg(this.AssociatedObject, this.currentResult, device);
                                arg.Tag = e.CumulativeManipulation.Translation;
                                this.OnGestureDetector(this.AssociatedObject, arg);
                            }
                            this.isGestureBehavior = true;
                            return(true);
                        }
                    }

                    else if (e.CumulativeManipulation.Translation.Length > GestureConsts.Current.MultiFingerDragThreshold && isZoom == false)
                    {
                        if (this.OnGestureDetector != null)
                        {
                            this.currentResult = GestureResult.OneFinger;
                            this.OnGestureDetector(this.AssociatedObject, new GestureArg(this.AssociatedObject, this.currentResult, device));
                        }
                        this.isGestureBehavior = true;
                        return(true);
                    }
                }
            }
            return(false);
        }
 public bool Last(TouchDevice point)
 {
     return(InputTracker.Last(point));
 }
Esempio n. 25
0
        /// <summary>
        /// Update the text description with the most recent property values. Position
        /// the textbox so that it does not go offscreen (outside parentGrid). Also
        /// position the connecting line between the touch device and the textbox.
        /// </summary>
        /// <param name="parentGrid">the container for this diagram-
        /// description text will not go outside of this container's bounds</param>
        /// <param name="touchDevice">the touch device to diagram</param>
        /// <param name="showTouchDeviceInfo">Whether or not the touch device info will be visible</param>
        private void UpdateDescription(Grid parentGrid, TouchDevice touchDevice, bool showTouchDeviceInfo)
        {
            // Show or hide the touchDevice info based on showTouchDeviceInfo
            Description.Visibility    = showTouchDeviceInfo ? Visibility.Visible : Visibility.Hidden;
            ConnectingLine.Visibility = showTouchDeviceInfo ? Visibility.Visible : Visibility.Hidden;

            if (!showTouchDeviceInfo)
            {
                // Don't need to do the calculations if info isn't going to be shown
                return;
            }

            Point position = touchDevice.GetPosition(parentGrid);
            Rect  bounds   = new Rect(0, 0, parentGrid.ActualWidth, parentGrid.ActualHeight);
            // Determine where around the touchDevice the description should be displayed.
            // The default position is above and to the left.
            bool isAbove = true;
            bool isLeft  = true;

            // Description text for tags is different than non-tags
            double descriptionXDistance;
            bool   isTag = touchDevice.GetIsTagRecognized();

            if (isTag)
            {
                descriptionXDistance = tagDescriptionXDistance;
            }
            else
            {
                descriptionXDistance = nonTagDescriptionXDistance;
            }

            // Put description below touchDevice if default location is out of bounds.
            Rect upperLeftBounds = GetDescriptionBounds(position, isAbove, isLeft, descriptionXDistance, descriptionYDistance);

            if (upperLeftBounds.Top < bounds.Top)
            {
                isAbove = false;
            }

            // Put description to the right of the touchDevice if default location is out of bounds.
            if (upperLeftBounds.Left < bounds.Left)
            {
                isLeft = false;
            }

            // Calculate the final bounds that will be used for the textbox position
            // based on the updated isAbove and isLeft values.
            Rect finalBounds = GetDescriptionBounds(position, isAbove, isLeft, descriptionXDistance, descriptionYDistance);

            Canvas.SetLeft(Description, finalBounds.Left);
            Canvas.SetTop(Description, finalBounds.Top);

            // Set the justification of the type in the textbox based
            // on which side of the touchDevice the textbox is on.
            if (isLeft)
            {
                Description.TextAlignment = TextAlignment.Right;
            }
            else
            {
                Description.TextAlignment = TextAlignment.Left;
            }

            // Create the description string.
            StringBuilder descriptionText = new StringBuilder();

            descriptionText.AppendLine(String.Format(CultureInfo.InvariantCulture, "RecognizedTypes: {0}", GetTouchDeviceTypeString(touchDevice)));
            descriptionText.AppendLine(String.Format(CultureInfo.InvariantCulture, "Id: {0}", touchDevice.Id));

            // Use the "f1" format specifier to limit the amount of decimal positions shown.
            descriptionText.AppendLine(String.Format(CultureInfo.InvariantCulture, "X: {0}", position.X.ToString("f1", CultureInfo.InvariantCulture)));
            descriptionText.AppendLine(String.Format(CultureInfo.InvariantCulture, "Y: {0}", position.Y.ToString("f1", CultureInfo.InvariantCulture)));

            // Display "null" for Orientation if the touchDevice does not have an orientation value.
            string orientationString;
            double?touchDeviceOrientation = touchDevice.GetOrientation(parentGrid);

            if (touchDeviceOrientation == null)
            {
                orientationString = "null";
            }
            else
            {
                orientationString = ((double)touchDeviceOrientation).ToString("f1", CultureInfo.InvariantCulture);
            }
            descriptionText.AppendLine(String.Format(CultureInfo.InvariantCulture, "Orientation: {0}", orientationString));

            if (touchDevice.GetTagData() != TagData.None)
            {
                descriptionText.AppendLine("Schema: 0x" + touchDevice.GetTagData().Schema.ToString("x8", CultureInfo.InvariantCulture));
                descriptionText.AppendLine("Series:  0x" + touchDevice.GetTagData().Series.ToString("x16", CultureInfo.InvariantCulture));
                descriptionText.AppendLine("ExtendedValue: 0x" + touchDevice.GetTagData().ExtendedValue.ToString("x16", CultureInfo.InvariantCulture));
                descriptionText.AppendLine("Value:  0x" + touchDevice.GetTagData().Value.ToString("x16", CultureInfo.InvariantCulture));
            }

            // Update the description textbox.
            Description.Text = descriptionText.ToString();

            // Update the line that connects the touchDevice to the description textbox.
            double x2;

            if (isLeft)
            {
                x2 = finalBounds.Right;
            }
            else
            {
                x2 = finalBounds.Left;
            }
            // Position (X1,Y1) is the center of the touchDevice.
            // Position (X2,Y2) is the edge of the description text box.
            ConnectingLine.X1 = position.X;
            ConnectingLine.Y1 = position.Y;
            ConnectingLine.X2 = x2;
            ConnectingLine.Y2 = finalBounds.Top + finalBounds.Height * 0.5;
        }