Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Image Manipulation in Multitouch Development

, 3 Apr 2010 CPOL
In this article, I will describe Image manipulation in Windows 7 multitouch Environment
Win7MultitouchDemo.zip
Win7MultitouchDemo
Win7MultitouchDemo.suo
Win7MultitouchDemo
images
Hydrangeas.jpg
Properties
Settings.settings
Windows7API
MultiTouchAPI
Windows7.Multitouch.WinForms
Properties
Windows7.Multitouch.WinForms.csproj.vspscc
Windows7.Multitouch.WPF
Properties
Windows7.Multitouch.WPF.csproj.vspscc
Windows7.Multitouch
Properties
Windows7.Multitouch.csproj.vspscc
Win7MultitouchDemo_1_.zip
Win7_20MultiTouchAPI_1_.zip
Win7_MultiTouchAPI.zip
MultiTouchAPI
Windows7.Multitouch.WinForms
Properties
Windows7.Multitouch.WinForms.csproj.vspscc
Windows7.Multitouch.WPF
Properties
Windows7.Multitouch.WPF.csproj.vspscc
Windows7.Multitouch
Properties
Windows7.Multitouch.csproj.vspscc
//-----------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------

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

namespace Windows7.Multitouch
{
    /// <summary>
    /// Handles touch events for a hWnd
    /// </summary>
    public class TouchHandler : Handler 
    {
        private bool _disablePalmRejection;

        internal TouchHandler(IHwndWrapper hWndWrapper)
            : base(hWndWrapper)
        {
        }

        /// <summary>
        /// Enabling this flag disables palm rejection
        /// </summary>
        public bool DisablePalmRejection
        {
            get
            {
                return _disablePalmRejection;
            }
            set
            {
                if (_disablePalmRejection == value)
                    return;

                _disablePalmRejection = value;

                if (HWndWrapper.IsHandleCreated)
                {
                    User32.UnregisterTouchWindow(HWndWrapper.Handle);
                    SetHWndTouchInfo();
                }
            }
        }

        /// <summary>
        /// Register for touch event
        /// </summary>
        /// <returns>true if succeeded</returns>
        protected override bool SetHWndTouchInfo()
        {
            return User32.RegisterTouchWindow(ControlHandle, _disablePalmRejection ? User32.TouchWindowFlag.WantPalm : 0);
        }

        /// <summary>
        /// Intercept and fire touch events
        /// </summary>
        /// <param name="hWnd">The Windows Handle</param>
        /// <param name="msg">Windows Message</param>
        /// <param name="wparam">wParam</param>
        /// <param name="lparam">lParam</param>
        /// <returns></returns>
        protected override uint WindowProc(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam)
        {
            switch (msg)
            {
                case User32.WM_TOUCH:

                    foreach (TouchEventArgs arg in DecodeMessage(hWnd, msg, wparam, lparam, DpiX, DpiY))
                    {

                        if (TouchDown != null && arg.IsTouchDown)
                        {
                            TouchDown(this, arg);
                        }
                    
                        if (TouchMove != null && arg.IsTouchMove)
                        {
                            TouchMove(this, arg);
                        }
                    
                        if (TouchUp != null && arg.IsTouchUp)
                        {
                            TouchUp(this, arg);
                        }
                    }
                    return 1; //handled
            }
            return 0;
        }


        /// <summary>
        /// Decode the message and create a collection of event arguments
        /// </summary>
        /// <remarks>
        /// One Windows message can result a group of events
        /// </remarks>
        /// <returns>An enumerator of thr resuting events</returns>
        /// <param name="hWnd">the WndProc hWnd</param>
        /// <param name="msg">the WndProc msg</param>
        /// <param name="wParam">the WndProc wParam</param>
        /// <param name="lParam">the WndProc lParam</param>
        private IEnumerable<TouchEventArgs> DecodeMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, float dpiX, float dpiY)
        {
            // More than one touchinput may be associated with a touch message,
            // so an array is needed to get all event information.
            int inputCount = User32.LoWord(wParam.ToInt32()); // Number of touch inputs, actual per-contact messages

            TOUCHINPUT[] inputs; // Array of TOUCHINPUT structures
            inputs = new TOUCHINPUT[inputCount]; // Allocate the storage for the parameters of the per-contact messages
            try
            {
                // Unpack message parameters into the array of TOUCHINPUT structures, each
                // representing a message for one single contact.
                if (!User32.GetTouchInputInfo(lParam, inputCount, inputs, Marshal.SizeOf(inputs[0])))
                {
                    // Get touch info failed.
                    throw new Exception("Error calling GetTouchInputInfo API");
                }

                // For each contact, dispatch the message to the appropriate message
                // handler.
                // Note that for WM_TOUCHDOWN you can get down & move notifications
                // and for WM_TOUCHUP you can get up & move notifications
                // WM_TOUCHMOVE will only contain move notifications
                // and up & down notifications will never come in the same message
                for (int i = 0; i < inputCount; i++)
                {
                    TouchEventArgs touchEventArgs = new TouchEventArgs(HWndWrapper, dpiX, dpiY, ref inputs[i]);
                    yield return touchEventArgs;
                }
            }
            finally
            {
                User32.CloseTouchInputHandle(lParam);
            }
        }
       

        // Touch event handlers

        /// <summary>
        /// Register to receive TouchDown Events
        /// </summary>
        public event EventHandler<TouchEventArgs> TouchDown;   // touch down event handler

        /// <summary>
        /// Register to receive TouchUp Events
        /// </summary>
        public event EventHandler<TouchEventArgs> TouchUp;     // touch up event handler

        /// <summary>
        /// Register to receive TouchMove Events
        /// </summary>
        public event EventHandler<TouchEventArgs> TouchMove;   // touch move event handler
    }


    /// <summary>
    /// EventArgs passed to Touch handlers 
    /// </summary>
    public class TouchEventArgs : EventArgs
    {
        private readonly IHwndWrapper _hWndWrapper;
        private readonly float _dpiXFactor;
        private readonly float _dpiYFactor;

        /// <summary>
        /// Create new touch event argument instance
        /// </summary>
        /// <param name="hWndWrapper">The target control</param>
        /// <param name="touchInput">one of the inner touch input in the message</param>
        internal TouchEventArgs(IHwndWrapper hWndWrapper, float dpiX, float dpiY, ref TOUCHINPUT touchInput)
        {
            _hWndWrapper = hWndWrapper;
            _dpiXFactor = 96F / dpiX;
            _dpiYFactor = 96F / dpiY;
            DecodeTouch(ref touchInput);
        }

        private bool CheckFlag(int value)
        {
            return (Flags & value) != 0;
        }

        

        // Decodes and handles WM_TOUCH* messages.
        private void DecodeTouch(ref TOUCHINPUT touchInput)
        {
            // TOUCHINFO point coordinates and contact size is in 1/100 of a pixel; convert it to pixels.
            // Also convert screen to client coordinates.
            if ( (touchInput.dwMask & User32.TOUCHINPUTMASKF_CONTACTAREA) != 0)
                ContactSize = new Size(AdjustDpiX(touchInput.cyContact / 100), AdjustDpiY(touchInput.cyContact / 100));
                          
            Id = touchInput.dwID;

            Point p = _hWndWrapper.PointToClient(new Point(touchInput.x / 100, touchInput.y / 100));
            Location = new Point(AdjustDpiX(p.X), AdjustDpiY(p.Y));

            Time = touchInput.dwTime;
            TimeSpan ellapse = TimeSpan.FromMilliseconds(Environment.TickCount - touchInput.dwTime);
            AbsoluteTime = DateTime.Now - ellapse;
           
            Mask = touchInput.dwMask;
            Flags = touchInput.dwFlags;
        }


        private int AdjustDpiX(int value)
        {
            return (int)(value * _dpiXFactor);
        }

        private int AdjustDpiY(int value)
        {
            return (int)(value * _dpiYFactor);
        }
      
        /// <summary>
        /// Touch client coordinate in pixels
        /// </summary>
        public Point Location {get; private set; }

        /// <summary>
        /// A touch point identifier that distinguishes a particular touch input
        /// </summary>
        public int Id { get; private set; }
       
        /// <summary>
        /// A set of bit flags that specify various aspects of touch point
        /// press, release, and motion. 
        /// </summary>
        public int Flags { get; private set; }
        
        /// <summary>
        /// mask which fields in the structure are valid
        /// </summary>
        public int Mask { get; private set; }

        /// <summary>
        /// touch event time
        /// </summary>
        public DateTime AbsoluteTime { get; private set; }

        /// <summary>
        /// touch event time from system up
        /// </summary>
        public int Time { get; private set; }
        
        /// <summary>
        /// the size of the contact area in pixels
        /// </summary>
        public Size? ContactSize { get; private set; }

        /// <summary>
        /// Is Primary Contact (The first touch sequence)
        /// </summary>
        public bool IsPrimaryContact
        {
            get { return (Flags & User32.TOUCHEVENTF_PRIMARY) != 0; }
        }

        /// <summary>
        /// Specifies that movement occurred
        /// </summary>
        public bool IsTouchMove
        {
            get { return CheckFlag(User32.TOUCHEVENTF_MOVE); }
        }

        /// <summary>
        /// Specifies that the corresponding touch point was established through a new contact
        /// </summary>
        public bool IsTouchDown
        {
            get { return CheckFlag(User32.TOUCHEVENTF_DOWN); }
        }

        /// <summary>
        /// Specifies that a touch point was removed
        /// </summary>
        public bool IsTouchUp
        {
            get { return CheckFlag(User32.TOUCHEVENTF_UP); }
        }

        /// <summary>
        /// Specifies that a touch point is in range
        /// </summary>
        public bool IsTouchInRange
        {
            get { return CheckFlag(User32.TOUCHEVENTF_INRANGE); }
        }

        /// <summary>
        /// specifies that this input was not coalesced.
        /// </summary>
        public bool IsTouchNoCoalesce
        {
            get { return CheckFlag(User32.TOUCHEVENTF_NOCOALESCE); }
        }

        /// <summary>
        /// Specifies that the touch point is associated with a pen contact
        /// </summary>
        public bool IsTouchPen
        {
            get { return CheckFlag(User32.TOUCHEVENTF_PEN); }
        }

        /// <summary>
        /// The touch event came from the user's palm
        /// </summary>
        /// <remarks>Set <see cref="DisablePalmRejection"/> to true</remarks>
        public bool IsTouchPalm
        {
            get { return CheckFlag(User32.TOUCHEVENTF_PALM); }
        }
    }
}

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)

Share

About the Author

Kunal Chowdhury (@kunal2383)
Team Leader
India India
Kunal Chowdhury is a Microsoft "Windows Platform Development" MVP (Most Valuable Professional), a Codeproject Mentor, Telerik MVP, Nokia Developer Champion, Speaker in various Microsoft events, Author, passionate Blogger and a Software Engineer 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 Store (WinRT) app development. He posts his findings, articles, tutorials in his technical blog and CodeProject.
 
Technical Blog: http://www.kunal-chowdhury.com
Facebook: http://facebook.com/blog.kunal
Twitter : http://twitter.com/kunal2383
Follow on   Twitter   Google+   LinkedIn

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 3 Apr 2010
Article Copyright 2010 by Kunal Chowdhury (@kunal2383)
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid