Before we go any further, I recommend heading over to software.intel.com and reading Detecting Slate/Clamshell Mode & Screen Orientation in a 2 in 1.
The code we’re looking to implement is basically this (done in a WndProc):
Simply stated, we need to monitor WM_SETTINGCHANGE or equivalent, detect which state we’re in, and respond accordingly.
Now that we know what our goal is, let’s make it work in a modern WPF C# application. I looked a few approaches before settling on my solution, and hopefully it will make sense why I’ve done it this way as we work through the code.
Our first step is to import user32.dll so we can define and use the GetSystemMetrics method.
To do that we’ll need to reference the InteropServices:
using System.Runtime.InteropServices; At this point you may have noticed that we don’t have the SystemMetric enum defined. We could create our own with just the two metrics we need, but we might as well jump over to www.pinvoke.net and get a fairly complete enum, and just copy that into our window class or somewhere else convenient (this might just be useful for other purposes as well):
Unfortunately this list doesn’t contain the two metrics we want to check against as we saw in the Intel doc, namely
SM_CONVERTABLESLATEMODE and SM_SYSTEMDOCKED
so we’ll add these to the end of the enum ourselves:
(* after writing this post I decided I should update the pinvoke.net page so the enum should now be up to date)
We’re almost ready to watch for the 2 in 1 state change. In C++ we would just capture the WM_SETTINGSCHANGED message.
We could do this in C# by overriding OnSourceInitialized() and hooking into the WndProc. Truth be told this was my first approach. But on my machine I wasn’t receiving “SystemDockMode” from the lParam – it would always give me “ConvertableSlateMode” regardless of the state.
So I decided to do it the “C# way” and just watch UserPreferenceChanging. To try it out just add
to the constructor and create a method with the correct signature. You’ll need to include “using Microsoft.Win32;” for SystemEvents.
We’ll get a UserPreferenceChanging event every time the device changes state, and it will come in under the General Category of UserPreferenceCategory. This is passed in with the UserPreferenceChagingEventArgs. Unfortunately we don’t get any additional information, and we don’t have access to the lParam as we would if we were handling the message directly.
And that’s where the GetSystemMetrics() method comes in. We’ll simply query for the state every time we change comes in under the General category. Uou’ll likely want to add some logic to remember your current state and only update your UI if it changes.
See the code below:
And that should give you everything you need to get started making 2 in 1 C# apps in WPF.
Finally, there’s a sample project with all of the above code on github here: