WPF/Enable-Multitouch.ps1

function Enable-MultiTouch
{
    <#
    .Synopsis
        Enables multiple touch events on a window
    .Description
        Registers a window for multiple touch events and creates
        three buffers (TouchStarts,TouchStops,TouchMoves) that
        will contain all of the touch events that have occured within
        a buffer window.
        This enables raw multitouch support, but does not
        enable gestures such as pinching or zooming
    .Parameter Window
        The Window to Enable for multitouch events
    .Parameter Buffer
        The time buffer to record event
    .Example
     
     
New-Window -WindowState Maximized -Resource @{
    Styluses=@{}
} -On_Loaded {
    $this |
        Enable-MultiTouch
} -On_StylusDown {
    $styluses = $this.Resources.Styluses
    $origin = $_.GetPosition($this.Content)
    $color = 'Black', 'Pink', 'Red', 'Blue', 'Green', 'Orange','DarkRed', 'MidnightBlue', 'Maroon', 'SaddleBrown' |
        Get-Random
     
    $line = New-Polyline -Stroke $color -StrokeThickness 3 -Points { $origin }
    $styluses.($_.StylusDevice.ID) = @{
        Line = $line
    }
    $line |
        Add-ChildControl $this.Content
} -On_StylusMove {
    $styluses = $this.Resources.Styluses
    $line = $styluses.($_.StylusDevice.ID).Line
    $point = $_.GetPosition($this.Content)
    $null = $line.Points.Add($point)
} -On_StylusUp {
    $styluses = $this.Resources.Styluses
    $styluses.($_.StylusDevice.ID).Line |
        Move-Control -fadeOut -duration ([timespan]::FromMilliseconds(500))
    $styluses.Remove($_.StylusDevice.ID)
} -Content {
    New-Canvas
} -asJob
    #>

    param(
    [Parameter(ValueFromPipeline=$true, 
        Mandatory=$true)]
    [Windows.Window]
    $Window,
    
    [Timespan]
    $Buffer = [Timespan]::FromSeconds(30)
    )
    begin {
        if (-not ('WPK.MT' -as [TYPE])) {
            $referencedAssemblies = 'WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
        'PresentationCore, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35',
        'PresentationFramework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
            Add-Type 'MT' -Namespace WPK -IgnoreWarnings `
            -ReferencedAssemblies $referencedAssemblies `
            -UsingNamespace System.Windows, System.Windows.Interop `
            -MemberDefinition '
[DllImport("user32")]
public static extern bool SetProp(IntPtr hWnd, string lpString, IntPtr hData);
'
,'
/// <summary>
/// Enable Stylus events, that represent touch events.
/// </summary>
/// <remarks>Each stylus device has an Id that is corelate to the touch Id</remarks>
/// <param name="window">The WPF window that needs stylus events</param>
public static void EnableStylusEvents(System.Windows.Window window)
{
    WindowInteropHelper windowInteropHelper = new WindowInteropHelper(window);
 
    // Set the window property to enable multitouch input on inking context.
    SetProp(windowInteropHelper.Handle, "MicrosoftTabletPenServiceProperty", new IntPtr(0x01000000));
}
'
 
        }
    }
    process {
        [WPK.MT]::EnableStylusEvents($window)
        $LinkedListType = "Collections.Generic.LinkedList"

        $window.Resources.TouchStarts = New-Object "$LinkedListType[PSObject]"
        $window.Resources.TouchStops = New-Object "$LinkedListType[PSObject]"
        $window.Resources.TouchMoves = New-Object "$LinkedListType[PSObject]"
        $window.Resources.TouchBuffer = $Buffer
        $window.add_StylusUp({
            $object = $_ |
                Add-Member NoteProperty Sender $this -PassThru |
                Add-Member NoteProperty TimeGenerated ([DateTime]::Now) -PassThru
            $TouchStops =$this.Resources.TouchStops
            $Buffer = $this.Resources.TouchBuffer
            $check = $TouchStops.First
            $time = $check.Value.TimeGenerated
            while ($time -and 
                (($time.Add($Buffer)) -lt (Get-Date))) {
                $oldCheck = $check
                $check = $check.Next
                if (-not $check) { return } 
                $time = $check.Value.TimeGenerated
                $null = $TouchStops.Remove($oldCheck)
                $oldCheck = $null
            }
            $null = $TouchStops.AddLast($Object)            
        })
        $window.add_StylusDown({
            $object = $_ |
                Add-Member NoteProperty Sender $this -PassThru |
                Add-Member NoteProperty TimeGenerated ([DateTime]::Now) -PassThru
            $TouchStarts =$this.Resources.TouchStarts
            $Buffer = $this.Resources.TouchBuffer
            $check = $TouchStarts.First
            $time = $check.Value.TimeGenerated
            while ($time -and 
                (($time.Add($Buffer)) -lt (Get-Date))) {
                $oldCheck = $check
                $check = $check.Next
                if (-not $check) { return } 
                $time = $check.Value.TimeGenerated
                $null = $TouchStarts.Remove($oldCheck)
                $oldCheck = $null
            }
            $null = $TouchStarts.AddLast($Object)            
        })
        $window.add_StylusMove({
            $object = $_ |
                Add-Member NoteProperty Sender $this -PassThru |
                Add-Member NoteProperty TimeGenerated ([DateTime]::Now) -PassThru
            $TouchMoves =$this.Resources.TouchMoves
            $Buffer = $this.Resources.TouchBuffer
            $check = $TouchMoves.First
            $time = $check.Value.TimeGenerated
            while ($time -and 
                (($time.Add($Buffer)) -lt (Get-Date))) {
                $oldCheck = $check
                $check = $check.Next
                if (-not $check) { return } 
                $time = $check.Value.TimeGenerated
                $null = $TouchMoves.Remove($oldCheck)
                $oldCheck = $null
            }
            $null = $TouchMoves.AddLast($Object)            
        })                
    }
}