Erkennen und Nachverfolgen mehrerer Berührungspunkte

In den folgenden Schritten wird erläutert, wie Sie mehrere Touchpunkte mithilfe von Windows Touch nachverfolgen.

  1. Erstellen Sie eine Anwendung, und aktivieren Sie Windows Touch.
  2. Fügen Sie einen Handler für WM_TOUCH - und Nachverfolgungspunkte hinzu.
  3. Zeichnen Sie die Punkte.

Nachdem Sie Ihre Anwendung ausgeführt haben, werden Kreise unter jeder Berührung gerendert. Der folgende Screenshot zeigt, wie Ihre Anwendung während der Ausführung aussehen könnte.

Screenshot einer Anwendung, die Touchpunkte als grüne und gelbe Kreise rendert

Erstellen einer Anwendung und Aktivieren der Windows-Toucheingabe

Beginnen Sie mit einer Microsoft Win32-Anwendung mit dem Microsoft Visual Studio-Assistenten. Nachdem Sie den Assistenten abgeschlossen haben, fügen Sie Unterstützung für Windows Touch-Nachrichten hinzu, indem Sie die Windows-Version in targetver.h festlegen und windows.h und windowsx.h in Ihrer Anwendung einschließen. Der folgende Code zeigt, wie die Windows-Version in targetver.h festgelegt wird.

#ifndef WINVER                  // Specifies that the minimum required platform is Windows 7.
#define WINVER 0x0601           // Change this to the appropriate value to target other versions of Windows.
#endif

#ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows 7.
#define _WIN32_WINNT 0x0601     // Change this to the appropriate value to target other versions of Windows.
#endif     

#ifndef _WIN32_WINDOWS          // Specifies that the minimum required platform is Windows 98.
#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
#endif

#ifndef _WIN32_IE                       // Specifies that the minimum required platform is Internet Explorer 7.0.
#define _WIN32_IE 0x0700        // Change this to the appropriate value to target other versions of IE.
#endif

Der folgende Code zeigt, wie Ihre Include-Direktiven hinzugefügt werden sollen. Außerdem können Sie einige globale Variablen erstellen, die später verwendet werden.

#include <windows.h>    // included for Windows Touch
#include <windowsx.h>   // included for point conversion

#define MAXPOINTS 10

// You will use this array to track touch points
int points[MAXPOINTS][2];

// You will use this array to switch the color / track ids
int idLookup[MAXPOINTS];


// You can make the touch points larger
// by changing this radius value
static int radius      = 50;

// There should be at least as many colors
// as there can be touch points so that you
// can have different colors for each point
COLORREF colors[] = { RGB(153,255,51), 
                      RGB(153,0,0), 
                      RGB(0,153,0), 
                      RGB(255,255,0), 
                      RGB(255,51,204), 
                      RGB(0,0,0),
                      RGB(0,153,0), 
                      RGB(153, 255, 255), 
                      RGB(153,153,255), 
                      RGB(0,51,153)
                    };

Hinzufügen eines Handlers für WM_TOUCH- und Nachverfolgpunkte

Deklarieren Sie zunächst einige Variablen, die vom WM_TOUCH-Handler in WndProc verwendet werden.

int wmId, wmEvent, i, x, y;

UINT cInputs;
PTOUCHINPUT pInputs;
POINT ptInput;   

Initialisieren Sie nun die Variablen, die zum Speichern von Touchpunkten verwendet werden, und registrieren Sie das Fenster für toucheingaben von der InitInstance-Methode .

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   HWND hWnd;

   hInst = hInstance; // Store instance handle in our global variable

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd) {
      return FALSE;
   }

   // register the window for touch instead of gestures
   RegisterTouchWindow(hWnd, 0);  

   // the following code initializes the points
   for (int i=0; i< MAXPOINTS; i++){
     points[i][0] = -1;
     points[i][1] = -1;
     idLookup[i]  = -1;
   }  

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}

Behandeln Sie als Nächstes die WM_TOUCH Nachricht von der WndProc-Methode . Der folgende Code zeigt eine Implementierung des Handlers für WM_TOUCH.

case WM_TOUCH:        
  cInputs = LOWORD(wParam);
  pInputs = new TOUCHINPUT[cInputs];
  if (pInputs){
    if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs, sizeof(TOUCHINPUT))){
      for (int i=0; i < static_cast<INT>(cInputs); i++){
        TOUCHINPUT ti = pInputs[i];
        index = GetContactIndex(ti.dwID);
        if (ti.dwID != 0 && index < MAXPOINTS){                            
          // Do something with your touch input handle
          ptInput.x = TOUCH_COORD_TO_PIXEL(ti.x);
          ptInput.y = TOUCH_COORD_TO_PIXEL(ti.y);
          ScreenToClient(hWnd, &ptInput);
          
          if (ti.dwFlags & TOUCHEVENTF_UP){                      
            points[index][0] = -1;
            points[index][1] = -1;                
          }else{
            points[index][0] = ptInput.x;
            points[index][1] = ptInput.y;                
          }
        }
      }

      InvalidateRect(hWnd, NULL, FALSE);
    }
    // If you handled the message and don't want anything else done with it, you can close it
    CloseTouchInputHandle((HTOUCHINPUT)lParam);
    delete [] pInputs;
  }else{
    // Handle the error here 
  }  

Hinweis

Um die ScreenToClient-Funktion verwenden zu können, müssen Sie in Ihrer Anwendung hohe DPI-Unterstützung haben. Weitere Informationen zur Unterstützung von hohen DPI-Werten finden Sie unter Hohe DPI.

Wenn ein Benutzer nun den Bildschirm berührt, werden die Positionen, die er berührt, im Punktearray gespeichert. Das dwID-Element der TOUCHINPUT-Struktur speichert einen Bezeichner, der hardwareabhängig ist.

Um das Problem zu beheben, dass der dwID-Member von der Hardware abhängig ist, verwendet der WM_TOUCH Case-Handler die Funktion GetContactIndex, die das dwID-Element der TOUCHINPUT-Struktur einem Punkt zuordnet, der auf dem Bildschirm gezeichnet wird. Der folgende Code zeigt eine Implementierung dieser Funktion.

// This function is used to return an index given an ID
int GetContactIndex(int dwID){
  for (int i=0; i < MAXPOINTS; i++){
    if (idLookup[i] == -1){
      idLookup[i] = dwID;
      return i;
    }else{
      if (idLookup[i] == dwID){
        return i;
      }
    }
  }
  // Out of contacts
  return -1;
}

Wichtig

Windows 11 und neuer

Einige Touchinteraktionen mit drei und vier Fingern funktionieren in Windows-Apps standardmäßig nicht mehr.

Standardmäßig werden Touchinteraktionen mit drei und vier Fingern jetzt vom System für Vorgänge wie das Wechseln oder Minimieren von Fenstern und das Ändern virtueller Desktops verwendet. Da diese Interaktionen nun auf Systemebene behandelt werden, könnte sich diese Änderung auf die Funktionalität Ihrer App auswirken.

Um Interaktionen mit drei oder vier Fingern innerhalb einer Anwendung zu unterstützen, wurde eine neue Benutzereinstellung eingeführt, die angibt, ob das System diese Interaktionen verarbeitet oder nicht:

Bluetooth-Geräte &> Touch > "Drei- und Vier-Finger-Touch-Gesten"

Wenn "Ein" (Standard) festgelegt ist, verarbeitet das System alle Drei- und Vierfingerinteraktionen (Apps können diese nicht unterstützen).

Wenn sie auf "Off" festgelegt ist, können Interaktionen mit drei und vier Fingern von Apps unterstützt werden (sie werden nicht vom System verarbeitet).

Wenn Ihre Anwendung diese Interaktionen unterstützen muss, empfiehlt es sich, Benutzer über diese Einstellung zu informieren und einen Link bereitzustellen, der die Einstellungs-App zur entsprechenden Seite (ms-settings:devices-touch) startet. Weitere Informationen finden Sie unter Launcher.LaunchUriAsync-Methode.

Zeichnen der Punkte

Deklarieren Sie die folgenden Variablen für die Zeichnungsroutine.

    // For double buffering
    static HDC memDC       = 0;
    static HBITMAP hMemBmp = 0;
    HBITMAP hOldBmp        = 0;
   
    // For drawing / fills
    PAINTSTRUCT ps;
    HDC hdc;
    HBRUSH hBrush;
    
    // For tracking dwId to points
    int index;

Das MemDC des Speicheranzeigekontexts wird zum Speichern eines temporären Grafikkontexts verwendet, der durch den gerenderten Anzeigekontext hdc ausgetauscht wird, um Flimmern zu vermeiden. Implementieren Sie die Zeichnungsroutine, die die von Ihnen gespeicherten Punkte übernimmt und einen Kreis an den Punkten zeichnet. Der folgende Code zeigt, wie Sie den WM_PAINT-Handler implementieren können.

  case WM_PAINT:
    hdc = BeginPaint(hWnd, &ps);    
    RECT client;
    GetClientRect(hWnd, &client);        
  
    // start double buffering
    if (!memDC){
      memDC = CreateCompatibleDC(hdc);
    }
    hMemBmp = CreateCompatibleBitmap(hdc, client.right, client.bottom);
    hOldBmp = (HBITMAP)SelectObject(memDC, hMemBmp);          

    hBrush = CreateSolidBrush(RGB(255, 255, 255));
    FillRect(memDC, &client, hBrush);
    DeleteObject(hBrush);

    //Draw Touched Points                
    for (i=0; i < MAXPOINTS; i++){
      hBrush = CreateSolidBrush(colors[i]);        
      SelectObject( memDC, hBrush);           

      x = points[i][0];
      y = points[i][1];
      if  (x >0 && y>0){              
        Ellipse(memDC, x - radius, y - radius, x+ radius, y + radius);
      }

      DeleteObject(hBrush);
    }
  
    BitBlt(hdc, 0,0, client.right, client.bottom, memDC, 0,0, SRCCOPY);      
    EndPaint(hWnd, &ps);

    SelectObject(memDC, hOldBmp);
    DeleteObject(hMemBmp);

    break;

Wenn Sie Ihre Anwendung ausführen, sollte sie nun in etwa wie die Abbildung am Anfang dieses Abschnitts aussehen.

Aus Spaß können Sie einige zusätzliche Linien um die Touchpunkte zeichnen. Der folgende Screenshot zeigt, wie die Anwendung mit ein paar zusätzlichen Linien aussehen könnte, die um die Kreise gezeichnet wurden.

Screenshot einer Anwendung, die Berührungspunkte als Kreise mit Linien durch die Mittelpunkte gerendert und die Kanten der Berührungspunkte überschneiden

Windows-Toucheingabe