TouchDevice.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Input / TouchDevice.cs / 1305600 / TouchDevice.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
//--------------------------------------------------------------------------- 

using System; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Windows; 
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Threading; 
using System.Security;
using System.Security.Permissions; 
using MS.Internal; 
using MS.Internal.KnownBoxes;
using MS.Internal.PresentationCore; 
using MS.Utility;
using SR = MS.Internal.PresentationCore.SR;
using SRID = MS.Internal.PresentationCore.SRID;
 

namespace System.Windows.Input 
{ 
    /// 
    ///     Represents a touch device (i.e. a finger). 
    /// 
    /// 
    ///     InheritanceDemand - Prevents subclassing TouchDevice from partial trust code.
    ///     This is for defense in depth since there is no scenario where partial trust code 
    ///     needs to inherit from this class.
    ///  
    [UIPermission(SecurityAction.InheritanceDemand, Unrestricted = true)] 
    public abstract class TouchDevice : InputDevice, IManipulator
    { 
        /// 
        ///     Instantiates a new instance of this class.
        /// 
        ///  
        ///     The ID of this device.
        ///     For a particular subclass of TouchDevice, ID should be unique. 
        ///     Note: This is not validated to be unique. 
        /// 
        ///  
        ///     Critical: Retrieves an InputManager instance.
        ///     PublicOK: Does not expose the InputManager.
        /// 
        [SecurityCritical] 
        protected TouchDevice(int deviceId)
            : base() 
        { 
            _deviceId = deviceId;
            _inputManager = InputManager.UnsecureCurrent; 
        }

        /// 
        ///     Critical: Attaches to InputManager event handlers. 
        ///     TreatAsSafe: Does not expose the InputManager.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private void AttachTouchDevice()
        { 
            _inputManager.PostProcessInput += new ProcessInputEventHandler(PostProcessInput);
            _inputManager.HitTestInvalidatedAsync += new EventHandler(OnHitTestInvalidatedAsync);
        }
 
        /// 
        ///     Critical: Detaches from InputManager event handlers. 
        ///     TreatAsSafe: Does not expose the InputManager. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void DetachTouchDevice()
        {
            _inputManager.PostProcessInput -= new ProcessInputEventHandler(PostProcessInput);
            _inputManager.HitTestInvalidatedAsync -= new EventHandler(OnHitTestInvalidatedAsync); 
        }
 
        ///  
        ///     The ID of this device.
        ///     For a particular subclass of TouchDevice, ID should be unique. 
        /// 
        public int Id
        {
            get { return _deviceId; } 
        }
 
        ///  
        ///     This event will be raised whenever the device gets activated
        ///  
        public event EventHandler Activated;

        /// 
        ///     This event will be raised whenever the device gets deactivated 
        /// 
        public event EventHandler Deactivated; 
 
        /// 
        ///     IsActive boolean 
        /// 
        public bool IsActive
        {
            get 
            {
                return _isActive; 
            } 
        }
 

        #region InputDevice

        ///  
        ///     Returns the element that input from this device is sent to.
        ///  
        ///  
        ///     Always the same value as DirectlyOver.
        ///  
        public sealed override IInputElement Target
        {
            get { return _directlyOver; }
        } 

        ///  
        ///     Returns the PresentationSource that is reporting input for this device. 
        /// 
        ///  
        ///     Callers must have UIPermission(UIPermissionWindow.AllWindows) to call this API.
        ///
        ///     Subclasses should use SetActiveSource to set this property.
        ///  
        ///
        ///     Critical - Accesses critical data (_activeSource) 
        ///     PublicOK - There is a demand. 
        ///
        public sealed override PresentationSource ActiveSource 
        {
            [SecurityCritical]
            get
            { 
                SecurityHelper.DemandUIWindowPermission();
                return _activeSource; 
            } 
        }
 
        /// 
        ///     Critical - PresentationSource is critical data.
        ///     PublicOK - This method has a link demand.
        ///  
        [SecurityCritical]
        [UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)] 
        protected void SetActiveSource(PresentationSource activeSource) 
        {
            _activeSource = activeSource; 
        }

        #endregion
 
        #region Location
 
        ///  
        ///     Returns the element that this device is over.
        ///  
        public IInputElement DirectlyOver
        {
            get { return _directlyOver; }
        } 

        ///  
        ///     Provides the current position. 
        /// 
        /// Defines the coordinate space. 
        /// The current position in the coordinate space of relativeTo.
        public abstract TouchPoint GetTouchPoint(IInputElement relativeTo);

        ///  
        ///     Provides all of the known points the device hit since the last reported position update.
        ///  
        /// Defines the coordinate space. 
        /// A list of points in the coordinate space of relativeTo.
        public abstract TouchPointCollection GetIntermediateTouchPoints(IInputElement relativeTo); 

        /// 
        ///     Critical - Access _activeSource.
        ///     TreatAsSafe - Does not expose _activeSource. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private IInputElement CriticalHitTest(Point point, bool isSynchronize) 
        {
            IInputElement over = null; 

            if (_activeSource != null)
            {
                switch (_captureMode) 
                {
                    case CaptureMode.None: 
                        // No capture, do a regular hit-test. 
                        if (_isDown)
                        { 
                            if (isSynchronize)
                            {
                                // In a synchronize call, we need to hit-test the window in addition to the element
                                over = GlobalHitTest(point, _activeSource); 
                            }
                            else 
                            { 
                                // Just hit-test the element
                                over = LocalHitTest(point, _activeSource); 
                            }

                            EnsureValid(ref over);
                        } 
                        break;
 
                    case CaptureMode.Element: 
                        // Capture is to a specific element, so the device will always be over that element.
                        over = _captured; 
                        break;

                    case CaptureMode.SubTree:
                        // Capture is set to an entire subtree. Hit-test to determine the element (and window) 
                        // the device is over. If the element is within the captured sub-tree (which can span
                        // multiple windows), then the device is over that element. If the element is not within 
                        // the sub-tree, then the device is over the captured element. 
                        {
                            IInputElement capture = InputElement.GetContainingInputElement(_captured as DependencyObject); 
                            if (capture != null)
                            {
                                // We need to re-hit-test to get the "real" UIElement we are over.
                                // This allows us to have our capture-to-subtree span multiple windows. 

                                // GlobalHitTest always returns an IInputElement, so we are sure to have one. 
                                over = GlobalHitTest(point, _activeSource); 
                            }
 
                            EnsureValid(ref over);

                            // Make sure that the element we hit is acutally underneath
                            // our captured element.  Because we did a global hit test, we 
                            // could have hit an element in a completely different window.
                            // 
                            // Note that we support the child being in a completely different window. 
                            // So we use the GetUIParent method instead of just looking at
                            // visual/content parents. 
                            if (over != null)
                            {
                                IInputElement ieTest = over;
                                while ((ieTest != null) && (ieTest != _captured)) 
                                {
                                    UIElement eTest = ieTest as UIElement; 
 
                                    if (eTest != null)
                                    { 
                                        ieTest = InputElement.GetContainingInputElement(eTest.GetUIParent(true));
                                    }
                                    else
                                    { 
                                        ContentElement ceTest = ieTest as ContentElement;
 
                                        if (ceTest != null) 
                                        {
                                            ieTest = InputElement.GetContainingInputElement(ceTest.GetUIParent(true)); 
                                        }
                                        else
                                        {
                                            UIElement3D e3DTest = (UIElement3D)ieTest; 
                                            ieTest = InputElement.GetContainingInputElement(e3DTest.GetUIParent(true));
                                        } 
                                    } 
                                }
 
                                if (ieTest != _captured)
                                {
                                    // If we missed the capture point, consider the device over the capture point.
                                    over = _captured; 
                                }
                            } 
                            else 
                            {
                                // If we didn't hit anything, consider the device over the capture point. 
                                over = _captured;
                            }
                        }
                        break; 
                }
            } 
 
            return over;
        } 

        private static void EnsureValid(ref IInputElement element)
        {
            // We understand UIElements and ContentElements. 
            // If we are over something else (like a raw visual) find the containing element.
            if ((element != null) && !InputElement.IsValid(element)) 
            { 
                element = InputElement.GetContainingInputElement(element as DependencyObject);
            } 
        }

        private static IInputElement GlobalHitTest(Point pt, PresentationSource inputSource)
        { 
            return MouseDevice.GlobalHitTest(false, pt, inputSource);
        } 
 
        private static IInputElement LocalHitTest(Point pt, PresentationSource inputSource)
        { 
            return MouseDevice.LocalHitTest(false, pt, inputSource);
        }

        #endregion 

        #region Capture 
 
        /// 
        ///     The element this device is currently captured to. 
        /// 
        /// 
        ///     This value affects hit-testing to determine DirectlyOver.
        ///  
        public IInputElement Captured
        { 
            get { return _captured; } 
        }
 
        /// 
        ///     The type of capture being used.
        /// 
        ///  
        ///     This value affects hit-testing to determine DirectlyOver.
        ///  
        public CaptureMode CaptureMode 
        {
            get { return _captureMode; } 
        }

        /// 
        ///     Captures this device to a particular element using CaptureMode.Element. 
        /// 
        /// The element this device will be captured to. 
        /// true if capture was changed, false otherwise. 
        public bool Capture(IInputElement element)
        { 
            return Capture(element, CaptureMode.Element);
        }

        ///  
        ///     Captures this device to a particular element.
        ///  
        /// The element this device will be captured to. 
        /// The type of capture to use.
        /// true if capture was changed, false otherwise. 
        public bool Capture(IInputElement element, CaptureMode captureMode)
        {
            VerifyAccess();
 
            // If the element is null or captureMode is None, ensure
            // that the other parameter is consistent. 
            if ((element == null) || (captureMode == CaptureMode.None)) 
            {
                element = null; 
                captureMode = CaptureMode.None;
            }

            UIElement uiElement; 
            ContentElement contentElement;
            UIElement3D uiElement3D; 
            CastInputElement(element, out uiElement, out contentElement, out uiElement3D); 

            if ((element != null) && (uiElement == null) && (contentElement == null) && (uiElement3D == null)) 
            {
                throw new ArgumentException(SR.Get(SRID.Invalid_IInputElement, element.GetType()), "element");
            }
 
            if (_captured != element)
            { 
                // Ensure that the new element is visible and enabled 
                if ((element == null) ||
                    (((uiElement != null) && uiElement.IsVisible && uiElement.IsEnabled) || 
                    ((contentElement != null) && contentElement.IsEnabled) ||
                    ((uiElement3D != null) && uiElement3D.IsVisible && uiElement3D.IsEnabled)))
                {
                    IInputElement oldCapture = _captured; 
                    _captured = element;
                    _captureMode = captureMode; 
 
                    UIElement oldUIElement;
                    ContentElement oldContentElement; 
                    UIElement3D oldUIElement3D;
                    CastInputElement(oldCapture, out oldUIElement, out oldContentElement, out oldUIElement3D);

                    if (oldUIElement != null) 
                    {
                        oldUIElement.IsEnabledChanged -= OnReevaluateCapture; 
                        oldUIElement.IsVisibleChanged -= OnReevaluateCapture; 
                        oldUIElement.IsHitTestVisibleChanged -= OnReevaluateCapture;
                    } 
                    else if (oldContentElement != null)
                    {
                        oldContentElement.IsEnabledChanged -= OnReevaluateCapture;
                    } 
                    else if (oldUIElement3D != null)
                    { 
                        oldUIElement3D.IsEnabledChanged -= OnReevaluateCapture; 
                        oldUIElement3D.IsVisibleChanged -= OnReevaluateCapture;
                        oldUIElement3D.IsHitTestVisibleChanged -= OnReevaluateCapture; 
                    }
                    if (uiElement != null)
                    {
                        uiElement.IsEnabledChanged += OnReevaluateCapture; 
                        uiElement.IsVisibleChanged += OnReevaluateCapture;
                        uiElement.IsHitTestVisibleChanged += OnReevaluateCapture; 
                    } 
                    else if (contentElement != null)
                    { 
                        contentElement.IsEnabledChanged += OnReevaluateCapture;
                    }
                    else if (uiElement3D != null)
                    { 
                        uiElement3D.IsEnabledChanged += OnReevaluateCapture;
                        uiElement3D.IsVisibleChanged += OnReevaluateCapture; 
                        uiElement3D.IsHitTestVisibleChanged += OnReevaluateCapture; 
                    }
 
                    UpdateReverseInheritedProperty(/* capture = */ true, oldCapture, _captured);

                    if (oldCapture != null)
                    { 
                        DependencyObject o = oldCapture as DependencyObject;
                        o.SetValue(UIElement.AreAnyTouchesCapturedPropertyKey, 
                            BooleanBoxes.Box(AreAnyTouchesCapturedOrDirectlyOver(oldCapture, /* isCapture = */ true))); 
                    }
                    if (_captured != null) 
                    {
                        DependencyObject o = _captured as DependencyObject;
                        o.SetValue(UIElement.AreAnyTouchesCapturedPropertyKey, BooleanBoxes.TrueBox);
                    } 

                    if (oldCapture != null) 
                    { 
                        RaiseLostCapture(oldCapture);
                    } 
                    if (_captured != null)
                    {
                        RaiseGotCapture(_captured);
                    } 

                    // Capture successfully moved, notify the subclass. 
                    OnCapture(element, captureMode); 

                    Synchronize(); 
                    return true;
                }
                else
                { 
                    return false;
                } 
            } 
            else
            { 
                return true;
            }
        }
 
        private void UpdateReverseInheritedProperty(bool capture, IInputElement oldElement, IInputElement newElement)
        { 
            // 
            List others = null;
            int count = (_activeDevices != null) ? _activeDevices.Count : 0; 
            if (count > 0)
            {
                others = new List(count);
            } 
            for (int i = 0; i < count; i++)
            { 
                TouchDevice touchDevice = _activeDevices[i]; 
                if (touchDevice != this)
                { 
                    DependencyObject other = capture ? (touchDevice._captured as DependencyObject) : (touchDevice._directlyOver as DependencyObject);
                    if (other != null)
                    {
                        others.Add(other); 
                    }
                } 
            } 

            ReverseInheritProperty property = capture ? (ReverseInheritProperty)UIElement.TouchesCapturedWithinProperty : (ReverseInheritProperty)UIElement.TouchesOverProperty; 
            DeferredElementTreeState treeState = capture ? _capturedWithinTreeState : _directlyOverTreeState;
            Action originChangedAction = capture ? null : RaiseTouchEnterOrLeaveAction;

            property.OnOriginValueChanged(oldElement as DependencyObject, newElement as DependencyObject, others, ref treeState, originChangedAction); 

            if (capture) 
            { 
                _capturedWithinTreeState = treeState;
            } 
            else
            {
                _directlyOverTreeState = treeState;
            } 
        }
 
        internal static void ReevaluateCapturedWithin(DependencyObject element, DependencyObject oldParent, bool isCoreParent) 
        {
            int count = _activeDevices != null ? _activeDevices.Count : 0; 
            for (int i = 0; i < count; i++)
            {
                TouchDevice touchDevice = _activeDevices[i];
                touchDevice.ReevaluateCapturedWithinAsync(element, oldParent, isCoreParent); 
            }
        } 
 
        private void ReevaluateCapturedWithinAsync(DependencyObject element, DependencyObject oldParent, bool isCoreParent)
        { 
            if (element != null)
            {
                if (_capturedWithinTreeState == null)
                { 
                    _capturedWithinTreeState = new DeferredElementTreeState();
                } 
 
                if (isCoreParent)
                { 
                    _capturedWithinTreeState.SetCoreParent(element, oldParent);
                }
                else
                { 
                    _capturedWithinTreeState.SetLogicalParent(element, oldParent);
                } 
            } 

            if (_reevaluateCapture == null) 
            {
                _reevaluateCapture = Dispatcher.BeginInvoke(DispatcherPriority.Input,
                    (DispatcherOperationCallback)delegate(object args)
                    { 
                        _reevaluateCapture = null;
                        OnReevaluateCapturedWithinAsync(); 
                        return null; 
                    }, null);
            } 
        }

        private void OnReevaluateCapturedWithinAsync()
        { 
            if (_captured == null)
            { 
                return; 
            }
 
            bool killCapture = false;

            //
            // First, check things like IsEnabled, IsVisible, etc. on a 
            // UIElement vs. ContentElement basis.
            // 
            UIElement uiElement; 
            ContentElement contentElement;
            UIElement3D uiElement3D; 
            CastInputElement(_captured, out uiElement, out contentElement, out uiElement3D);
            if (uiElement != null)
            {
                killCapture = !uiElement.IsEnabled || !uiElement.IsVisible || !uiElement.IsHitTestVisible; 
            }
            else if (contentElement != null) 
            { 
                killCapture = !contentElement.IsEnabled;
            } 
            else if (uiElement3D != null)
            {
                killCapture = !uiElement3D.IsEnabled || !uiElement3D.IsVisible || !uiElement3D.IsHitTestVisible;
            } 
            else
            { 
                killCapture = true; 
            }
 
            //
            // Second, if we still haven't thought of a reason to kill capture, validate
            // it on a Visual basis for things like still being in the right tree.
            // 
            if (killCapture == false)
            { 
                DependencyObject containingVisual = InputElement.GetContainingVisual(_captured as DependencyObject); 
                killCapture = !ValidateVisualForCapture(containingVisual);
            } 

            //
            // Lastly, if we found any reason above, kill capture.
            // 
            if (killCapture)
            { 
                Capture(null); 
            }
 
            // Refresh AreAnyTouchCapturesWithinProperty so that ReverseInherited flags are updated.
            if ((_capturedWithinTreeState != null) && !_capturedWithinTreeState.IsEmpty)
            {
                UpdateReverseInheritedProperty(/* capture = */ true, _captured, _captured); 
            }
        } 
 
        /// 
        ///     Critical: This code accesses critical data (_activeSource) 
        ///     TreatAsSafe: Although it accesses critical data it does not modify or expose it, only compares against it.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private bool ValidateVisualForCapture(DependencyObject visual) 
        {
            if (visual == null) 
                return false; 

            PresentationSource presentationSource = PresentationSource.CriticalFromVisual(visual); 

            return ((presentationSource != null) && (presentationSource == _activeSource));
        }
 
        private void OnReevaluateCapture(object sender, DependencyPropertyChangedEventArgs e)
        { 
            // IsEnabled, IsVisible, and/or IsHitTestVisible became false 
            if (!(bool)e.NewValue)
            { 
                if (_reevaluateCapture == null)
                {
                    _reevaluateCapture = Dispatcher.BeginInvoke(DispatcherPriority.Input,
                        (DispatcherOperationCallback)delegate(object args) 
                        {
                            _reevaluateCapture = null; 
                            Capture(null); 
                            return null;
                        }, null); 
                }
            }
        }
 
        private static void CastInputElement(IInputElement element, out UIElement uiElement, out ContentElement contentElement, out UIElement3D uiElement3D)
        { 
            uiElement = element as UIElement; 
            contentElement = (uiElement == null) ? element as ContentElement : null;
            uiElement3D = ((uiElement == null) && (contentElement == null)) ? element as UIElement3D : null; 
        }

        /// 
        ///     Critical - Accesses _inputManager. 
        ///     TreatAsSafe - Does not expose _inputManager and event raised is not critical.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private void RaiseLostCapture(IInputElement oldCapture)
        { 
            Debug.Assert(oldCapture != null, "oldCapture should be non-null.");

            TouchEventArgs e = CreateEventArgs(Touch.LostTouchCaptureEvent);
            e.Source = oldCapture; 
            _inputManager.ProcessInput(e);
        } 
 
        /// 
        ///     Critical - Accesses _inputManager. 
        ///     TreatAsSafe - Does not expose _inputManager and event raised is not critical.
        /// 
        [SecurityCritical, SecurityTreatAsSafe]
        private void RaiseGotCapture(IInputElement captured) 
        {
            Debug.Assert(captured != null, "captured should be non-null."); 
 
            TouchEventArgs e = CreateEventArgs(Touch.GotTouchCaptureEvent);
            e.Source = captured; 
            _inputManager.ProcessInput(e);
        }

        ///  
        ///     Notifies subclasses that capture changed.
        ///  
        ///  
        /// 
        protected virtual void OnCapture(IInputElement element, CaptureMode captureMode) 
        {
        }

        #endregion 

        #region Updating State 
 
        protected bool ReportDown()
        { 
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, EventTrace.Event.TouchDownReported, _deviceId);

            _isDown = true;
            UpdateDirectlyOver(/* isSynchronize = */ false); 
            bool handled = RaiseTouchDown();
            OnUpdated(); 
 
            Touch.ReportFrame();
            return handled; 
        }

        protected bool ReportMove()
        { 
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, EventTrace.Event.TouchMoveReported, _deviceId);
 
            UpdateDirectlyOver(/* isSynchronize = */ false); 
            bool handled = RaiseTouchMove();
            OnUpdated(); 

            Touch.ReportFrame();
            return handled;
        } 

        protected bool ReportUp() 
        { 
            EventTrace.EasyTraceEvent(EventTrace.Keyword.KeywordInput | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, EventTrace.Event.TouchUpReported, _deviceId);
 
            bool handled = RaiseTouchUp();
            _isDown = false;
            UpdateDirectlyOver(/* isSynchronize = */ false);
            OnUpdated(); 

            Touch.ReportFrame(); 
            return handled; 
        }
 
        protected void Activate()
        {
            if (_isActive)
            { 
                throw new InvalidOperationException(SR.Get(SRID.Touch_DeviceAlreadyActivated));
            } 
 
            PromotingToManipulation = false;
            AddActiveDevice(this); 
            AttachTouchDevice();
            Synchronize();

            if (_activeDevices.Count == 1) 
            {
                _isPrimary = true; 
            } 

            _isActive = true; 

            if (Activated != null)
            {
                Activated(this, EventArgs.Empty); 
            }
        } 
 
        protected void Deactivate()
        { 
            if (!_isActive)
            {
                throw new InvalidOperationException(SR.Get(SRID.Touch_DeviceNotActivated));
            } 

            Capture(null); 
 
            DetachTouchDevice();
            RemoveActiveDevice(this); 

            _isActive = false;
            _manipulatingElement = null;
 
            if (Deactivated != null)
            { 
                Deactivated(this, EventArgs.Empty); 
            }
        } 

        /// 
        ///     Forces the TouchDevice to resynchronize.
        ///  
        /// 
        ///     Critical: Accesses _activeSource 
        ///     PublicOK: Does not expose _activeSource. 
        /// 
        [SecurityCritical] 
        public void Synchronize()
        {
            if (_activeSource != null &&
                _activeSource.CompositionTarget != null && 
                !_activeSource.CompositionTarget.IsDisposed)
            { 
                if (UpdateDirectlyOver(/* isSynchronize = */ true)) 
                {
                    OnUpdated(); 
                    Touch.ReportFrame();
                }
            }
        } 

        ///  
        ///     Critical: Calling this method would do mouse promotions. 
        ///     PublicOK: This method has a demand on it.
        ///     Demand:   Technically the demand is not needed because the 
        ///               user can already do this indirectly by canceling the
        ///               manipulation. But the decision is to limit the scope
        ///               of this raw method to full trust.
        ///  
        [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)]
        protected virtual void OnManipulationEnded(bool cancel) 
        { 
            UIElement manipulatableElement = GetManipulatableElement();
            if (manipulatableElement != null && PromotingToManipulation) 
            {
                Capture(null);
            }
        } 

        protected virtual void OnManipulationStarted() 
        { 
        }
 
        private void OnHitTestInvalidatedAsync(object sender, EventArgs e)
        {
            // The hit-test result may have changed.
            Synchronize(); 

            if ((_directlyOverTreeState != null) && !_directlyOverTreeState.IsEmpty) 
            { 
                UpdateReverseInheritedProperty(/* capture = */ false, _directlyOver, _directlyOver);
            } 
        }

        private bool UpdateDirectlyOver(bool isSynchronize)
        { 
            IInputElement newDirectlyOver = null;
 
            TouchPoint touchPoint = GetTouchPoint(null); 
            if (touchPoint != null)
            { 
                Point position = touchPoint.Position;
                newDirectlyOver = CriticalHitTest(position, isSynchronize);
            }
 
            if (newDirectlyOver != _directlyOver)
            { 
                ChangeDirectlyOver(newDirectlyOver); 
                return true;
            } 
            else
            {
                return false;
            } 
        }
 
        private void OnReevaluateDirectlyOver(object sender, DependencyPropertyChangedEventArgs e) 
        {
            ReevaluateDirectlyOverAsync(null, null, true); 
        }

        internal static void ReevaluateDirectlyOver(DependencyObject element, DependencyObject oldParent, bool isCoreParent)
        { 
            int count = _activeDevices != null ? _activeDevices.Count : 0;
            for (int i = 0; i < count; i++) 
            { 
                TouchDevice touchDevice = _activeDevices[i];
                touchDevice.ReevaluateDirectlyOverAsync(element, oldParent, isCoreParent); 
            }
        }

        private void ReevaluateDirectlyOverAsync(DependencyObject element, DependencyObject oldParent, bool isCoreParent) 
        {
            if (element != null) 
            { 
                if (_directlyOverTreeState == null)
                { 
                    _directlyOverTreeState = new DeferredElementTreeState();
                }

                if (isCoreParent) 
                {
                    _directlyOverTreeState.SetCoreParent(element, oldParent); 
                } 
                else
                { 
                    _directlyOverTreeState.SetLogicalParent(element, oldParent);
                }
            }
 
            if (_reevaluateOver == null)
            { 
                _reevaluateOver = Dispatcher.BeginInvoke(DispatcherPriority.Input, 
                    (DispatcherOperationCallback)delegate(object args)
                    { 
                        _reevaluateOver = null;
                        OnHitTestInvalidatedAsync(this, EventArgs.Empty);
                        return null;
                    }, null); 
            }
        } 
 
        private void ChangeDirectlyOver(IInputElement newDirectlyOver)
        { 
            Debug.Assert(newDirectlyOver != _directlyOver, "ChangeDirectlyOver called when newDirectlyOver is the same as _directlyOver.");

            IInputElement oldDirectlyOver = _directlyOver;
            _directlyOver = newDirectlyOver; 

            UIElement oldUIElement; 
            ContentElement oldContentElement; 
            UIElement3D oldUIElement3D;
            CastInputElement(oldDirectlyOver, out oldUIElement, out oldContentElement, out oldUIElement3D); 
            UIElement newUIElement;
            ContentElement newContentElement;
            UIElement3D newUIElement3D;
            CastInputElement(newDirectlyOver, out newUIElement, out newContentElement, out newUIElement3D); 

            if (oldUIElement != null) 
            { 
                oldUIElement.IsEnabledChanged -= OnReevaluateDirectlyOver;
                oldUIElement.IsVisibleChanged -= OnReevaluateDirectlyOver; 
                oldUIElement.IsHitTestVisibleChanged -= OnReevaluateDirectlyOver;
            }
            else if (oldContentElement != null)
            { 
                oldContentElement.IsEnabledChanged -= OnReevaluateDirectlyOver;
            } 
            else if (oldUIElement3D != null) 
            {
                oldUIElement3D.IsEnabledChanged -= OnReevaluateDirectlyOver; 
                oldUIElement3D.IsVisibleChanged -= OnReevaluateDirectlyOver;
                oldUIElement3D.IsHitTestVisibleChanged -= OnReevaluateDirectlyOver;
            }
            if (newUIElement != null) 
            {
                newUIElement.IsEnabledChanged += OnReevaluateDirectlyOver; 
                newUIElement.IsVisibleChanged += OnReevaluateDirectlyOver; 
                newUIElement.IsHitTestVisibleChanged += OnReevaluateDirectlyOver;
            } 
            else if (newContentElement != null)
            {
                newContentElement.IsEnabledChanged += OnReevaluateDirectlyOver;
            } 
            else if (newUIElement3D != null)
            { 
                newUIElement3D.IsEnabledChanged += OnReevaluateDirectlyOver; 
                newUIElement3D.IsVisibleChanged += OnReevaluateDirectlyOver;
                newUIElement3D.IsHitTestVisibleChanged += OnReevaluateDirectlyOver; 
            }

            UpdateReverseInheritedProperty(/* capture = */ false, oldDirectlyOver, newDirectlyOver);
 
            if (oldDirectlyOver != null)
            { 
                DependencyObject o = oldDirectlyOver as DependencyObject; 
                o.SetValue(UIElement.AreAnyTouchesDirectlyOverPropertyKey,
                            BooleanBoxes.Box(AreAnyTouchesCapturedOrDirectlyOver(oldDirectlyOver, /* isCapture = */ false))); 
            }
            if (newDirectlyOver != null)
            {
                DependencyObject o = newDirectlyOver as DependencyObject; 
                o.SetValue(UIElement.AreAnyTouchesDirectlyOverPropertyKey, BooleanBoxes.TrueBox);
            } 
        } 

        ///  
        ///     Action to raise the TouchEnter/TouchLeave events.
        ///     This will be executed by TouchesOver RevereInheritance
        ///     property class on the entire parent chain affected by the
        ///     changes in TouchDevice's DirectlyOver. 
        /// 
        private Action RaiseTouchEnterOrLeaveAction 
        { 
            get
            { 
                if (_raiseTouchEnterOrLeaveAction == null)
                {
                    _raiseTouchEnterOrLeaveAction = new Action(RaiseTouchEnterOrLeave);
                } 
                return _raiseTouchEnterOrLeaveAction;
            } 
        } 

        ///  
        ///     Critical - Accesses _inputManager.
        ///     TreatAsSafe - Does not expose _inputManager and event raised is not critical.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void RaiseTouchEnterOrLeave(DependencyObject element, bool isLeave)
        { 
            Debug.Assert(element != null); 
            TouchEventArgs touchEventArgs = CreateEventArgs(isLeave ? Touch.TouchLeaveEvent : Touch.TouchEnterEvent);
            touchEventArgs.Source = element; 
            _inputManager.ProcessInput(touchEventArgs);
        }

        private TouchEventArgs CreateEventArgs(RoutedEvent routedEvent) 
        {
            // 
            TouchEventArgs touchEventArgs = new TouchEventArgs(this, Environment.TickCount); 
            touchEventArgs.RoutedEvent = routedEvent;
            return touchEventArgs; 
        }

        /// 
        ///     Critical - Accesses _inputManager. 
        ///     TreatAsSafe - Does not expose _inputManager and event raised is not critical.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private bool RaiseTouchDown()
        { 
            TouchEventArgs e = CreateEventArgs(Touch.PreviewTouchDownEvent);
            _lastDownHandled = false;
            _inputManager.ProcessInput(e);
 
            // We want to return true if either of PreviewTouchDown or TouchDown
            // events are handled. Hence we cannot use e.Handled which is only 
            // for preview. 
            return _lastDownHandled;
        } 

        /// 
        ///     Critical - Accesses _inputManager.
        ///     TreatAsSafe - Does not expose _inputManager and event raised is not critical. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private bool RaiseTouchMove() 
        {
            TouchEventArgs e = CreateEventArgs(Touch.PreviewTouchMoveEvent); 
            _lastMoveHandled = false;
            _inputManager.ProcessInput(e);

            // We want to return true if either of PreviewTouchMove or TouchMove 
            // events are handled. Hence we cannot use e.Handled which is only
            // for preview. 
            return _lastMoveHandled; 
        }
 
        /// 
        ///     Critical - Accesses _inputManager.
        ///     TreatAsSafe - Does not expose _inputManager and event raised is not critical.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private bool RaiseTouchUp() 
        { 
            TouchEventArgs e = CreateEventArgs(Touch.PreviewTouchUpEvent);
            _lastUpHandled = false; 
            _inputManager.ProcessInput(e);

            // We want to return true if either of PreviewTouchUp or TouchUp
            // events are handled. Hence we cannot use e.Handled which is only 
            // for preview.
            return _lastUpHandled; 
        } 

        #endregion 

        #region Input Processing and Promotion

        ///  
        ///     Critical: This method can be used for input spoofing
        ///  
        [SecurityCritical] 
        private void PostProcessInput(object sender, ProcessInputEventArgs e)
        { 
            InputEventArgs inputEventArgs = e.StagingItem.Input;
            if ((inputEventArgs != null) && (inputEventArgs.Device == this))
            {
                if (inputEventArgs.Handled) 
                {
                    RoutedEvent routedEvent = inputEventArgs.RoutedEvent; 
 
                    if (routedEvent == Touch.PreviewTouchMoveEvent ||
                        routedEvent == Touch.TouchMoveEvent) 
                    {
                        _lastMoveHandled = true;
                    }
                    else if (routedEvent == Touch.PreviewTouchDownEvent || 
                        routedEvent == Touch.TouchDownEvent)
                    { 
                        _lastDownHandled = true; 
                    }
                    else if (routedEvent == Touch.PreviewTouchUpEvent || 
                        routedEvent == Touch.TouchUpEvent)
                    {
                        _lastUpHandled = true;
                    } 
                }
                else 
                { 
                    bool forManipulation;
                    RoutedEvent promotedTouchEvent = PromotePreviewToMain(inputEventArgs.RoutedEvent, out forManipulation); 
                    if (promotedTouchEvent != null)
                    {
                        TouchEventArgs promotedTouchEventArgs = CreateEventArgs(promotedTouchEvent);
                        e.PushInput(promotedTouchEventArgs, e.StagingItem); 
                    }
                    else if (forManipulation) 
                    { 
                        UIElement manipulatableElement = GetManipulatableElement();
                        if (manipulatableElement != null) 
                        {
                            PromoteMainToManipulation(manipulatableElement, (TouchEventArgs)inputEventArgs);
                        }
                    } 
                }
            } 
        } 

        private RoutedEvent PromotePreviewToMain(RoutedEvent routedEvent, out bool forManipulation) 
        {
            forManipulation = false;
            if (routedEvent == Touch.PreviewTouchMoveEvent)
            { 
                return Touch.TouchMoveEvent;
            } 
            else if (routedEvent == Touch.PreviewTouchDownEvent) 
            {
                return Touch.TouchDownEvent; 
            }
            else if (routedEvent == Touch.PreviewTouchUpEvent)
            {
                return Touch.TouchUpEvent; 
            }
 
            forManipulation = (routedEvent == Touch.TouchMoveEvent) || 
                (routedEvent == Touch.TouchDownEvent) ||
                (routedEvent == Touch.TouchUpEvent) || 
                (routedEvent == Touch.GotTouchCaptureEvent) ||
                (routedEvent == Touch.LostTouchCaptureEvent);
            return null;
        } 

        private UIElement GetManipulatableElement() 
        { 
            UIElement element = InputElement.GetContainingUIElement(_directlyOver as DependencyObject) as UIElement;
            if (element != null) 
            {
                element = Manipulation.FindManipulationParent(element);
            }
 
            return element;
        } 
 
        private void PromoteMainToManipulation(UIElement manipulatableElement, TouchEventArgs touchEventArgs)
        { 
            RoutedEvent routedEvent = touchEventArgs.RoutedEvent;
            if (routedEvent == Touch.TouchDownEvent)
            {
                // When touch goes down or if we're in the middle of a move, capture so that we can 
                // start manipulation. We could be in the middle of a move if a device delayed
                // promotion, such as the StylusTouchDevice, due to other gesture detection. 
                Capture(manipulatableElement); 
            }
            else if ((routedEvent == Touch.TouchUpEvent) && PromotingToManipulation) 
            {
                // When touch goes up, release capture so that we can stop manipulation.
                Capture(null);
            } 
            else if ((routedEvent == Touch.GotTouchCaptureEvent) && !PromotingToManipulation)
            { 
                UIElement element = _captured as UIElement; 
                if (element != null && element.IsManipulationEnabled)
                { 
                    // When touch gets capture and if the captured element
                    // is manipulable, then add it as a manipulator to
                    // the captured element.
                    _manipulatingElement = new WeakReference(element); 
                    Manipulation.AddManipulator(element, this);
                    PromotingToManipulation = true; 
                    OnManipulationStarted(); 
                }
            } 
            else if ((routedEvent == Touch.LostTouchCaptureEvent) && PromotingToManipulation && _manipulatingElement != null)
            {
                UIElement element = _manipulatingElement.Target as UIElement;
                _manipulatingElement = null; 
                if (element != null)
                { 
                    // When touch loses capture, remove it as a manipulator. 
                    Manipulation.TryRemoveManipulator(element, this);
                    PromotingToManipulation = false; 
                }
            }
        }
 
        /// 
        ///     Whether this device should promote to manipulation. 
        ///  
        internal bool PromotingToManipulation
        { 
            get;
            private set;
        }
 
        #endregion
 
        #region Active Devices 

        private static void AddActiveDevice(TouchDevice device) 
        {
            if (_activeDevices == null)
            {
                _activeDevices = new List(2); 
            }
 
            _activeDevices.Add(device); 
        }
 
        private static void RemoveActiveDevice(TouchDevice device)
        {
            if (_activeDevices != null)
            { 
                _activeDevices.Remove(device);
            } 
        } 

        internal static TouchPointCollection GetTouchPoints(IInputElement relativeTo) 
        {
            TouchPointCollection points = new TouchPointCollection();
            if (_activeDevices != null)
            { 
                int count = _activeDevices.Count;
                for (int i = 0; i < count; i++) 
                { 
                    TouchDevice device = _activeDevices[i];
                    points.Add(device.GetTouchPoint(relativeTo)); 
                }
            }

            return points; 
        }
 
        internal static TouchPoint GetPrimaryTouchPoint(IInputElement relativeTo) 
        {
            if ((_activeDevices != null) && (_activeDevices.Count > 0)) 
            {
                TouchDevice device = _activeDevices[0];
                if (device._isPrimary)
                { 
                    return device.GetTouchPoint(relativeTo);
                } 
            } 

            return null; 
        }

        internal static void ReleaseAllCaptures(IInputElement element)
        { 
            if (_activeDevices != null)
            { 
                int count = _activeDevices.Count; 
                for (int i = 0; i < count; i++)
                { 
                    TouchDevice device = _activeDevices[i];
                    if (device.Captured == element)
                    {
                        device.Capture(null); 
                    }
                } 
            } 
        }
 
        internal static IEnumerable GetCapturedTouches(IInputElement element, bool includeWithin)
        {
            return GetCapturedOrOverTouches(element, includeWithin, /* isCapture = */ true);
        } 

        internal static IEnumerable GetTouchesOver(IInputElement element, bool includeWithin) 
        { 
            return GetCapturedOrOverTouches(element, includeWithin, /* isCapture = */ false);
        } 

        private static bool IsWithin(IInputElement parent, IInputElement child)
        {
            // We are assuming parent and child are Visual, Visual3D, or ContentElement 
            DependencyObject currentChild = child as DependencyObject;
            while ((currentChild != null) && (currentChild != parent)) 
            { 
                if (currentChild is Visual || currentChild is Visual3D)
                { 
                    currentChild = VisualTreeHelper.GetParent(currentChild);
                }
                else
                { 
                    currentChild = ((ContentElement)currentChild).Parent;
                } 
            } 

            return (currentChild == parent); 
        }

        private static IEnumerable GetCapturedOrOverTouches(IInputElement element, bool includeWithin, bool isCapture)
        { 
            List touches = new List();
            if (_activeDevices != null) 
            { 
                int count = _activeDevices.Count;
                for (int i = 0; i < count; i++) 
                {
                    TouchDevice device = _activeDevices[i];
                    IInputElement touchElement = isCapture ? device.Captured : device.DirectlyOver;
                    if ((touchElement != null) && 
                        ((touchElement == element) ||
                        (includeWithin && IsWithin(element, touchElement)))) 
                    { 
                        touches.Add(device);
                    } 
                }
            }
            return touches;
        } 

        private static bool AreAnyTouchesCapturedOrDirectlyOver(IInputElement element, bool isCapture) 
        { 
            if (_activeDevices != null)
            { 
                int count = _activeDevices.Count;
                for (int i = 0; i < count; i++)
                {
                    TouchDevice device = _activeDevices[i]; 
                    IInputElement touchElement = isCapture ? device.Captured : device.DirectlyOver;
                    if (touchElement != null && 
                        touchElement == element) 
                    {
                        return true; 
                    }
                }
            }
            return false; 
        }
 
        #endregion 

        #region IManipulator 

        int IManipulator.Id
        {
            get 
            {
                return Id; 
            } 
        }
 
        Point IManipulator.GetPosition(IInputElement relativeTo)
        {
            return GetTouchPoint(relativeTo).Position;
        } 

        public event EventHandler Updated; 
 
        private void OnUpdated()
        { 
            if (Updated != null)
            {
                Updated(this, EventArgs.Empty);
            } 
        }
 
        ///  
        ///     Critical: Calling this method would do mouse promotions.
        ///     PublicOK: This method has a demand on it. 
        ///     Demand:   Technically the demand is not needed because the
        ///               user can already do this indirectly by canceling the
        ///               manipulation. But the decision is to limit the scope
        ///               of this raw method to full trust. 
        /// 
        [SecurityCritical, UIPermissionAttribute(SecurityAction.LinkDemand, Unrestricted = true)] 
        void IManipulator.ManipulationEnded(bool cancel) 
        {
            this.OnManipulationEnded(cancel); 
        }

        #endregion
 
        #region Data
 
        private int _deviceId; 
        private IInputElement _directlyOver;
        private IInputElement _captured; 
        private CaptureMode _captureMode;
        private bool _isDown;
        private DispatcherOperation _reevaluateCapture;
        private DispatcherOperation _reevaluateOver; 
        private DeferredElementTreeState _directlyOverTreeState;
        private DeferredElementTreeState _capturedWithinTreeState; 
        private bool _isPrimary; 
        private bool _isActive;
        private Action _raiseTouchEnterOrLeaveAction; 

        // Technically only one flag is needed as per current code. But if
        // one of the derived touch devices raise a synchronizing TouchMove event,
        // while raising TouchUp, things would be messed up. Hence using three 
        // different flags.
        private bool _lastDownHandled; 
        private bool _lastUpHandled; 
        private bool _lastMoveHandled;
 
        /// 
        ///     Critical - PresentationSource must be protected.
        /// 
        [SecurityCritical] 
        private PresentationSource _activeSource;
 
        ///  
        ///     Critical - InputManager must be protected.
        ///  
        [SecurityCritical]
        private InputManager _inputManager;

        private WeakReference _manipulatingElement; 

        [ThreadStatic] 
        private static List _activeDevices; 

        #endregion 
    }
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK