Tablet App, Auto Rotation and Taskbar Height Adjus

2019-08-22 01:10发布

问题:

Self answer and hope to help others too working with tablet devices in Windows.

  1. Am I IN Tablet mode or not.

  2. Lock the orientation. In my case, the tablet has a built-in scanner that is in the same plane as the landscape mode of the tablet laying flat. We did not want users to have the screen keep flip-flopping while they may be carrying between locations and scans.

  3. That funky virtual keyboard. Apparently, the virtual keyboard can be docked into the taskbar area and thus change the visible real-estate of the main Windows bound dimensions for your screens. Then the virtual keyboard goes away and Windows bound dimensions change back.

To resolve tablet rotation, I give credit to How to lock tablet screen rotation in UWP app? here on S/O.

Below is the critical snippets of the process. Hope this helps others dabbling or struggling to find similar information for their development needs.

public class MyWindow : Window
{
    public MyWindow()
    {
        DetectForTabletMode();

        // Turn OFF auto-rotation for duration of my primary application window
        MyUser32.SetAutoRotation(false);

        // Hooks for detecting screen bounds / taskbar height impact to form
        // the "SystemEvents" is a static instance and always available.  I have added
        // so each window can adjust itself as needed.  I have actually hooked into
        // both User preferences and Display Settings
        SystemEvents.UserPreferenceChanged += MyWindow_DisplaySettingsChanged;
        SystemEvents.DisplaySettingsChanged += MyWindow_DisplaySettingsChanged;

        // hook to closed event so I can clean up when window is finished
        Closed += MyWindow_Closed;
    }

    private void MyWindow_Closed(object sender, EventArgs e)
    {
        // Disconnect event handler for detecting when in/out of tablet mode
        if(_tabletWatch != null )
        {
            _tabletWatch.EventArrived -= tabletWatch_EventArrived;
            _tabletWatch.Stop();
        }

        // When closing my main window, re-enable the auto-rotation
        MyUser32.SetAutoRotation(true);

        // Disable screen resolution impact to prevent memory leaks 
        // must release on closing window or memory leaks per MS documentation
        // https://docs.microsoft.com/en-us/dotnet/api/microsoft.win32.systemevents.displaysettingschanged?view=netframework-4.7.2
        SystemEvents.UserPreferenceChanged -= MyWindow_DisplaySettingsChanged;
        SystemEvents.DisplaySettingsChanged -= MyWindow_DisplaySettingsChanged;


        // under your control, probably only want to turn back on at the END of the APPLICATION
        // and not just all windows individually
        MyUser32.SetAutoRotation(true);

    }

    #region Detect / Set / Handle tablet mode

    private bool _inTabletMode;
    public bool InTabletMode
    {
        get { return _inTabletMode; }
        private set
        {
            _inTabletMode = value;
            // do anything you want when changing in/out of tablet mode
        }
    }

    private ManagementEventWatcher _tabletWatch;


    private void DetectForTabletMode()
    {
        var currentUser = WindowsIdentity.GetCurrent();
        // to capture event when user swaps between normal and tablet modes.
        if (currentUser != null && currentUser.User != null)
        {
            var wqlEventQuery = new EventQuery( string.Format(
                @"SELECT * 
                    FROM 
                        RegistryValueChangeEvent 
                    WHERE 
                            Hive='HKEY_USERS' 
                        AND KeyPath='{0}\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ImmersiveShell' 
                        AND ValueName='TabletMode'", currentUser.User.Value ));

            _tabletWatch = new ManagementEventWatcher(wqlEventQuery);
            _tabletWatch.EventArrived += tabletWatch_EventArrived;
            _tabletWatch.Start();
        }
    }

    private void tabletWatch_EventArrived(object sender, EventArrivedEventArgs e)
    {
        Dispatcher.Invoke(() =>
        {
            InTabletMode = 1 == (int)Registry.GetValue("HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ImmersiveShell", "TabletMode", 0);
        });
    }

    #endregion Detect / Set / Handle tablet mode

    #region Display and Orientation settings
    private System.Drawing.Rectangle _lastDimensions;

    // typical mode is landscape such as desktop / notbook mode anyhow.
    private bool _isLandscapeMode = true;   
    public bool IsLandscapeMode { get { return _isLandscapeMode; } }

    private bool _isPortraitMode { get { return !_isLandscapeMode; } }
    public bool IsPortraitMode { get { return _isPortraitMode; } }


    private void MyWindow_DisplaySettingsChanged(object sender, EventArgs e)
    {
        // get WINDOWS primary screen boundary dimensions
        var psb = System.Windows.Forms.Screen.PrimaryScreen.Bounds;

        // only if coming in and different than a previous setting
        if (_lastDimensions != null)
        {
            // Compare height and width of screen and act accordingly for orientation
            _isLandscapeMode = (psb.Height < psb.Width);

            // did dimensions change, such as from height of taskbar area shrinking
            // available real estate for YOUR Window.
            if (_lastDimensions.Height == psb.Height
                && _lastDimensions.Width == psb.Width)
                // same dimensions, get out, nothing more to do...
                return;

            // allow virtual method per form to handle itself as needed
            // and pass in the new dimensions.
            WinDimensionsChanged(psb);
        }
        // always preserve latest dimensions IN-CASE changes, such as taskbar, etc..
        _lastDimensions = psb;
    }

    protected virtual void WinDimensionsChanged(System.Drawing.Rectangle newestDimensions)
    {
        // do whatever you want per child-declared window

    }

    #endregion Display and Orientation settings

}