Click here to Skip to main content
15,881,757 members
Articles / Desktop Programming / WPF

Image Manipulation in Multitouch Development

Rate me:
Please Sign up or sign in to vote.
4.81/5 (8 votes)
3 Apr 2010CPOL3 min read 60.8K   2.5K   19  
In this article, I will describe Image manipulation in Windows 7 multitouch Environment
//-----------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using Windows7.Multitouch.Interop;
using System.Reflection;
using System.Threading;
using System.Drawing;

namespace Windows7.Multitouch
{
    /// <summary>
    /// Wrapp HWND source such as System.Windows.Forms.Control, or System.Windows.Window
    /// </summary>
    interface IHwndWrapper
    {
        /// <summary>
        /// The Underline Windows Handle
        /// </summary>
        IntPtr Handle { get; }

        /// <summary>
        /// The .NET object that wrap the Windows Handle (WinForm, WinForm Control, WPF Window, IntPtr of HWND)
        /// </summary>
        object Source { get; }

        /// <summary>
        /// The Win32 Handle has been created
        /// </summary>
        event EventHandler HandleCreated;

        /// <summary>
        /// /// The Win32 Handle has been destroyed
        /// </summary>
        event EventHandler HandleDestroyed;

        /// <summary>
        /// Check if the Win32 Handle is already created
        /// </summary>
        bool IsHandleCreated { get; }

        /// <summary>
        /// Computes the location of the specified screen point into client coordinates
        /// </summary>
        /// <param name="point">The screen coordinate System.Drawing.Point to convert</param>
        /// <returns>A point that represents the converted point in client coordinates</returns>
        Point PointToClient(Point point);
    }

    /// <summary>
    /// A Common interface foir timer.
    /// The timer has to be in the UI thread context
    /// </summary>
    public interface IGUITimer : IDisposable
    {
        /// <summary>
        /// Gets or sets whether the timer is running.
        /// </summary>
        bool Enabled { get; set; }
        
        /// <summary>
        /// Gets or sets the time, in milliseconds, before the Tick event is raised
        /// </summary>
        int Interval { get; set; }
        
        /// <summary>
        ///   Occurs when the specified timer interval has elapsed and the timer is enabled.
        /// </summary>
        event EventHandler Tick;

        /// <summary>
        /// Starts the timer.
        /// </summary>
        void Start();
        
        /// <summary>
        /// Stops the timer.
        /// </summary>
        void Stop();
    }

    
    /// <summary>
    /// Base class for handling Gesture and Touch event
    /// </summary>
    /// <remarks>
    /// A form can have one handler, either touch handler or gesture handler. 
    /// The form need to create the handler and register to events. 
    /// The code is not thread safe, we assume that the calling thread is the 
    /// UI thread. There is no blocking operation in the code.
    /// </remarks>
    public abstract class Handler
    {
        private readonly IHwndWrapper _hWndWrapper;
        static private List<object> _controlInUse = new List<object>();
        private User32.WindowProcDelegate _windowProcDelegate;
        private IntPtr _originalWindowProcId;
        
        /// <summary>
        /// Initiate touch support for the underline hWnd 
        /// </summary>
        /// <remarks>Registering the hWnd to touch support or configure the hWnd to receive gesture messages</remarks>
        /// <returns>true if succeeded</returns>
        protected abstract bool SetHWndTouchInfo();

        /// <summary>
        /// The interceptor WndProc
        /// </summary>
        /// <param name="hWnd">WndProc hWnd</param>
        /// <param name="msg">WndProc msg</param>
        /// <param name="wparam">WndProc wParam</param>
        /// <param name="lparam">WndProc lPara</param>
        /// <returns>WndProc return</returns>
        protected abstract uint WindowProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam);

        
        internal static T CreateHandler<T>(IHwndWrapper hWndWrapper) where T : Handler
        {
            if (_controlInUse.Contains(hWndWrapper.Source))
                throw new Exception("Only one handler can be registered for a control.");

            hWndWrapper.HandleDestroyed += (s, e) => _controlInUse.Remove(s);
            _controlInUse.Add(hWndWrapper.Source);

            return Activator.CreateInstance(typeof(T), BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance,
                null, new object [] { hWndWrapper }, Thread.CurrentThread.CurrentCulture) as T;
        }


        /// <summary>
        /// We create the hanlder using a factory method.
        /// </summary>
        /// <param name="hWndWrapper">The control or Window that registered for touch/gesture events</param>
        internal Handler(IHwndWrapper hWndWrapper)
        {
            _hWndWrapper = hWndWrapper;

            if (_hWndWrapper.IsHandleCreated)
                Initialize();
            else
                _hWndWrapper.HandleCreated += (s, e) => Initialize();

        }

        /// <summary>
        /// Connect the handler to the Control
        /// </summary>
        /// <remarks>
        /// The trick is to subclass the Control and intercept touch/gesture events, then reflect
        /// them back to the control.
        /// </remarks>
        private void Initialize()
        {
            if (!SetHWndTouchInfo())
            {
                throw new NotSupportedException("Cannot register window");
            }

            _windowProcDelegate = WindowProcSubClass;

            //According to the SDK doc SetWindowLongPtr should be exported by both 32/64 bit O/S
            //But it does not.
            _originalWindowProcId = IntPtr.Size == 4 ?
                User32.SubclassWindow(_hWndWrapper.Handle, User32.GWLP_WNDPROC, _windowProcDelegate) :
                User32.SubclassWindow64(_hWndWrapper.Handle, User32.GWLP_WNDPROC, _windowProcDelegate);

            //take the desktop DPI
            using (Graphics graphics = Graphics.FromHwnd(_hWndWrapper.Handle))
            {
                DpiX = graphics.DpiX;
                DpiY = graphics.DpiY;
            }

            WindowMessage += (s, e) => { };
        }

        /// <summary>
        /// Intercept touch/gesture events using Windows subclassing
        /// </summary>
        /// <param name="hWnd">The hWnd of the registered form</param>
        /// <param name="msg">The WM code</param>
        /// <param name="wparam">The WM WParam</param>
        /// <param name="lparam">The WM LParam</param>
        /// <returns></returns>
        private uint WindowProcSubClass(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
        {
            WindowMessage(this, new WMEventArgs(hWnd, msg, wparam, lparam));
            
            if (msg == User32.WM_GESTURENOTIFY && GestureNotify != null)
            {
                GestureNotify(this, new GestureNotifyEventArgs(lparam));
            }
            else
            {

                uint result = WindowProc(hWnd, msg, wparam, lparam);

                if (result != 0)
                    return result;
            }

            return User32.CallWindowProc(_originalWindowProcId, hWnd, msg, wparam, lparam);
        }


        /// <summary>
        /// The registered control wrapper
        /// </summary>
        internal IHwndWrapper HWndWrapper
        {
            get
            {
                return _hWndWrapper;
            }
        }

        /// <summary>
        /// The registered control's handler
        /// </summary>
        protected IntPtr ControlHandle
        {
            get
            {
                return _hWndWrapper.IsHandleCreated ? _hWndWrapper.Handle : IntPtr.Zero;
            }
        }

        /// <summary>
        /// The X DPI of the target window
        /// </summary>
        public float DpiX { get; private set; }

        /// <summary>
        /// The Y DPI of the target window
        /// </summary>
        public float DpiY { get; private set; }

        /// <summary>
        ///  GestureNotify event notifies a window that gesture recognition is
        //   in progress and a gesture will be generated if one is recognized under the
        //   current gesture settings.
        /// </summary>
        public event EventHandler<GestureNotifyEventArgs> GestureNotify;

        /// <summary>
        /// Enable advanced message handling/blocking
        /// </summary>
        internal event EventHandler<WMEventArgs> WindowMessage;

        /// <summary>
        /// Report digitizer capabilities
        /// </summary>
        public static class DigitizerCapabilities
        {
            /// <summary>
            /// Get the current Digitizer Status
            /// </summary>
            public static DigitizerStatus Status
            {
                get
                {
                    return (DigitizerStatus)User32.GetDigitizerCapabilities(User32.DigitizerIndex.SM_DIGITIZER);
                }
            }

            /// <summary>
            /// Get the maximum touches capability
            /// </summary>
            public static int MaxumumTouches
            {
                get
                {
                    return User32.GetDigitizerCapabilities(User32.DigitizerIndex.SM_MAXIMUMTOUCHES);
                }
            }

            /// <summary>
            /// Check for integrated touch support
            /// </summary>
            public static bool IsIntegratedTouch
            {
                get
                {
                    return (Status & DigitizerStatus.IntegratedTouch) != 0;
                }
            }

            /// <summary>
            /// Check for external touch support
            /// </summary>
            public static bool IsExternalTouch
            {
                get
                {
                    return (Status & DigitizerStatus.ExternalTouch) != 0;
                }
            }

            /// <summary>
            /// Check for Pen support
            /// </summary>
            public static bool IsIntegratedPan
            {
                get
                {
                    return (Status & DigitizerStatus.IntegratedPan) != 0;
                }
            }

            /// <summary>
            /// Check for external Pan support
            /// </summary>
            public static bool IsExternalPan
            {
                get
                {
                    return (Status & DigitizerStatus.ExternalPan) != 0;
                }
            }

            /// <summary>
            /// Check for multi-input
            /// </summary>
            public static bool IsMultiInput
            {
                get
                {
                    return (Status & DigitizerStatus.MultiInput) != 0;
                }
            }

            /// <summary>
            /// Check if touch device is ready
            /// </summary>
            public static bool IsStackReady
            {
                get
                {
                    return (Status & DigitizerStatus.StackReady) != 0;
                }
            }

            /// <summary>
            /// Check if Multi-touch support device is ready
            /// </summary>
            public static bool IsMultiTouchReady
            {
                get
                {
                    return (Status & (DigitizerStatus.StackReady | DigitizerStatus.MultiInput)) != 0;
                }
            }
        }

        /// <summary>
        /// Check if the Window is registered for multitouch events
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        public static bool IsTouchWindows(IntPtr hWnd)
        {
            uint cap;
            return User32.IsTouchWindow(hWnd, out cap);
        }
    }

    /// <summary>
    ///  GestureNotify event notifies a window that gesture recognition is
    //   in progress and a gesture will be generated if one is recognized under the
    //   current gesture settings.
    /// </summary>
    public class GestureNotifyEventArgs : EventArgs
    {
        internal GestureNotifyEventArgs(IntPtr lparam)
        {
            User32.GESTURENOTIFYSTRUCT gestureNotify = 
                (User32.GESTURENOTIFYSTRUCT)Marshal.PtrToStructure(lparam, typeof(User32.GESTURENOTIFYSTRUCT));
            
            //TODO: Do we need to be DPI aware also here? 
            Location = new Point(gestureNotify.ptsLocation.x, gestureNotify.ptsLocation.y);
            TargetHwnd = gestureNotify.hwndTarget;
        }

        /// <summary>
        /// The gesture location
        /// </summary>
        public Point Location { get; private set; }

        /// <summary>
        /// The gesture target window
        /// </summary>
        public IntPtr TargetHwnd { get; private set; }
    }

    /// <summary>
    /// Enable advanced message handling/blocking
    /// </summary>
    internal class WMEventArgs : EventArgs
    {
        public WMEventArgs(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
        {
            HWnd = hWnd;
            Message = msg;
            WParam = wparam;
            LParam = lparam;
        }

        public IntPtr HWnd { get; private set; }
        public int Message { get; private set; }
        public IntPtr WParam { get; private set; }
        public IntPtr LParam { get; private set; }
    }


#pragma warning disable 1591
    /// <summary>
    /// All availible digitizer capabilities
    /// </summary>
    [Flags]
    public enum DigitizerStatus : byte
    {
        IntegratedTouch = 0x01,
        ExternalTouch = 0x02,
        IntegratedPan = 0x04,
        ExternalPan = 0x08,
        MultiInput = 0x40,
        StackReady = 0x80
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
India India

Kunal Chowdhury is a former Microsoft "Windows Platform Development" MVP (Most Valuable Professional, 2010 - 2018), a Codeproject Mentor, Speaker in various Microsoft events, Author, passionate Blogger and a Senior Technical Lead by profession.

He is currently working in an MNC located in India. He has a very good skill over XAML, C#, Silverlight, Windows Phone, WPF and Windows app development. He posts his findings, articles, tutorials in his technical blog (www.kunal-chowdhury.com) and CodeProject.


Books authored:


Connect with Kunal on:





Comments and Discussions