WPF, Silverlight, Windows Phone 8 und Windows Store Apps

WPF: 2 in 1 – Erkennen ob Laptop oder Tablet
Kommentare

In der Kolumne „XAML Expertise“ präsentiert Gregor Biswanger Top-How-tos zum Thema XAML. Einsteiger und Fortgeschrittene XAML-Experten sollen hier durch geballtes Wissen gesättigt werden. Heute gibt es folgende Expertise: „WPF: 2 in 1 – Erkennen ob Laptop oder Tablet“. Viel Spaß mit XAML Expertise.

Mit Windows 8 ist eine ganz neue Geräteklasse auf den Markt gekommen: die so genannten Ultrabook Convertibles, etwa das Dell XPS 12 oder das Lenovo Yoga 2. Aus einem Notebook kann hier dank unterschiedlicher Mechanismen blitzschnell ein Tablet werden. Bildschirme lassen sich von der Tastatur lösen und werden so zu Tablets, in anderen Fällen werden Bildschirme gedreht, umklappt und ihre Rückseiten fungieren als Tablets, manchmal werden auch die Tastaturen umgeklappt. Je nach Hersteller unterscheiden sich die Verwandlungsmöglichkeiten. Intel stellt für Ultrabooks einen Treiber bereit, der den aktuellen Zustand – ob Laptop oder Tablet – bekannt gibt. Darauf können auch Windows-Anwendungen reagieren und dementsprechend ihre Oberfläche anpassen. Ein gelungenes Beispiel ist die Bildbearbeitungssoftware Krita Gemini. Wird sie auf dem Desktop gestartet, wird ein umfangreiches Menü für den Mauszeiger angezeigt, verwandelt sich der Laptop zum Tablet, ist die Software touchoptimiert.

Die Dokumentation zur Verwendung des API findet man bei Intel nur für C++. Heute zeige ich Ihnen aber, wie mittels P/Invoke ganz einfach auch unter WPF mit .NET in C# auf die Erkennungsfunktion zugegriffen wird.

In WPF die Erkennungsfunktion nutzen

Für das Beispiel wird eine leere WPF-Anwendung erzeugt. Der Einfachheit halber wird der Code direkt in der Code-Behind MainWindow.xaml.cs geschrieben. Anschließend kann er beliebig in eine eigene Komponente gebaut werden. Für die Schnittstelle zu den jeweiligen Treibern wird auf die user32.dll des Win32-API zugegriffen. Der Zugriff vom managed Code aus auf den nativen Code wird via P/Invoke ermöglicht. Dazu wird eine statische Methode mit dem Namen GetSystemMetrics geschrieben, und via Attribute vergeben wir die P/Invoke-Referenz der user32.dll:

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern int GetSystemMetrics(DeviceMode deviceMode);

Die SystemMetrics gibt systemspezifische Zustände der Oberfläche bekannt. Diese GetSystemMetrics-Methode wird gemeinsam mit einem Parameter vom Typ Integer aufgerufen. Die Zahlen stehen für systemrelevante Schnittstellen, eine Auflistung finden Sie auf der MSDN-Webseite. In unserem Falle ist SM_CONVERTIBLESLATEMODE wichtig, die über den Integer-Wert 8195 (0 x 2003) erreicht wird. Um angenehmer mit den Zahlen entwickeln zu können, wird ein passendes Enum dafür angelegt. In unserem Beispiel wird ein Enum mit dem Namen DeviceMode erzeugt:

public enum DeviceMode
{
  SM_CONVERTIBLESLATEMODE = 0x2003,
  SM_SYSTEMDOCKED = 0x2004
} 

Als Ergebnis erhalten wir einen Integer-Wert, der für ein nummerisches Boolean steht. Wir fügen in der WPF-Anwendung ein loaded Event hinzu. Hier können wir dann auf die neu erzeugte Methode zugreifen und passend dazu die Information ausgeben (Listing 1). Die Anwendung kann nun getestet werden.

public MainWindow()
{
  InitializeComponent();
  Loaded += MainWindow_Loaded;
}

void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
  if (Convert.ToBoolean(GetSystemMetrics(DeviceMode.SM_CONVERTIBLESLATEMODE)))
  {
    MessageBox.Show("In labtop Mode.");
  }
  else
  {
    MessageBox.Show("In tablet Mode.");
  }
}

Anschließend wird der Zustand zum Start der Software ermittelt. Im Demovideo der Bildbearbeitungssoftware Krita Gemini wird zur Laufzeit zum Tablet gewechselt. Das bedeutet, wir müssen auf Systemevents des Win32-API reagieren. Dabei hilft uns die HwndSource-Klasse, mit der wir auf die Win32-Instanz unserer WPF-Anwendung zugreifen. Zusätzlich benötigen wir eine statische Methode, die mit den allgemeinen Systemevents gekoppelt wird. Wird diese ausgelöst, überprüfen wir zuerst, ob es sich hierbei um die SystemMetrics handelt, die beim Integer-Wert 26 stehen (Listing 2).

public partial class MainWindow : Window
{
  public MainWindow()
  {
    InitializeComponent();

    Loaded += MainWindow_Loaded;
  }

  void MainWindow_Loaded(object sender, RoutedEventArgs e)
  {
    if (Convert.ToBoolean(GetSystemMetrics(DeviceMode.SM_CONVERTIBLESLATEMODE)))
    {
      MessageBox.Show("In labtop Mode.");
    }
    else
    {
      MessageBox.Show("In tablet Mode.");
    }

    HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
    source.AddHook(new HwndSourceHook(WndProc));
  }

  private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
  {
    if (msg == 26)
    {
      if (Convert.ToBoolean(GetSystemMetrics(DeviceMode.SM_CONVERTIBLESLATEMODE)))
      {
        MessageBox.Show("In laptop mode.");
      }
      else
      {
        MessageBox.Show("In tablet mode.");
      }

      if (Convert.ToBoolean(GetSystemMetrics(DeviceMode.SM_SYSTEMDOCKED)))
      {
        MessageBox.Show("System docked.");
      }
    }

    return IntPtr.Zero;
  }

  public enum DeviceMode
  {
    SM_CONVERTIBLESLATEMODE = 0x2003,
    SM_SYSTEMDOCKED = 0x2004
  }

  [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
  public static extern int GetSystemMetrics(DeviceMode deviceMode);
}
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -