Ускорение навигации в WinRT

87

Используете ли вы мышь с пятью кнопками вместо обычных трех? Я тоже не использую, но некоторые люди связали две дополнительные кнопки с командами перехода вперед и назад в Internet Explorer. Другие пользователи Internet Explorer привыкли выполнять переход вперед и назад комбинациями клавиш Att+<- и Att+->. На некоторых клавиатурах даже существуют специальные клавиши для выполнения этих операций. Возможно, вы захотите поддерживать те же комбинации клавиш для навигации в своих приложениях. Для этого понадобятся два события, которые нам ранее еще не встречались: PointerPressed и AcceleratorKeyActivated.

Событие AcceleratorKeyActivated недоступно в классах Page и Frame и даже в классе Window, встроенном в Frame. Зато оно доступно в CoreWindow - объекте, обеспечивающем получение событий ввода для Window, а вы можете получить объект CoreWindow для текущего объекта Window.

Обработчик AcceleratorKeyActivated первым получает доступ к нажатиям клавиш. Если нажатие будет идентифицировано как служебная комбинация клавиш, он может запретить дальнейшую обработку ввода приложением, задав свойству Handled в аргументах события значение true.

Событие PointerPressed инициируется по нажатию кнопки мыши или прикосновению к экрану пальцем или пером. Это событие определяется классом UIElement и наследуется классами Frame и Page, но обработчик этого события также можно определить и в CoreWindow.

Поскольку команды-ускорители от клавиатуры и мыши работают на более высоком уровне, чем страница, их обработку удобно разместить в классе App.

Ранее я приводил метод OnLaunched класса App из приложения ApplicationStateSave. Часть кода в конце метода была заменена многоточием. Рассмотрим этот код:

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    // ...
    
    // Добавленный код
    Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated 
        += OnAcceleratorKeyActivated;
    Window.Current.CoreWindow.PointerPressed += OnPointerPressed;
    // Конец добавленного кода
}

Обработчик события PointerPressed проще, поэтому мы начнем с него. Состояния всех пяти кнопок мыши доступны в свойстве Properties свойства CurrentPoint аргументов события. Две дополнительные кнопки, обычно используемые для навигации, обозначаются XButton1 и XButton2. Нас интересуют только ситуации, в которых все обычные кнопки не нажаты, а нажата только одна из дополнительных кнопок - то есть их состояния отличны друг от друга:

private void OnPointerPressed(CoreWindow sender, PointerEventArgs args)
{
    PointerPointProperties props = args.CurrentPoint.Properties;

    if (!props.IsMiddleButtonPressed &&
        !props.IsLeftButtonPressed &&
        !props.IsRightButtonPressed &&
        props.IsXButton1Pressed != props.IsXButton2Pressed)
    {
        if (props.IsXButton1Pressed)
            GoBack();
        else
            GoForward();

        args.Handled = true;
    }
}

Если событие приводит к вызову метода GoBack или GoForward, обработчик события задает свойству Handled аргументов события значение true. Для ввода с клавиатуры обработчик события может использовать значения перечисления VirtualKey для клавиш <- и ->, но VirtualKey не содержит значений для специальных комбинаций клавиш браузеров. В Win32 API они идентифицируются именами VK_BROWSER_BACK и VK_BROWSER_FORWARD со значениями 166 и 167 соответственно:

private void OnAcceleratorKeyActivated(CoreDispatcher sender, AcceleratorKeyEventArgs args)
{
    if ((args.EventType == CoreAcceleratorKeyEventType.KeyDown) ||
         args.EventType == CoreAcceleratorKeyEventType.SystemKeyDown &&
        (args.VirtualKey == VirtualKey.Left || 
        args.VirtualKey == VirtualKey.Right ||
        (int)args.VirtualKey == 166 || 
        (int)args.VirtualKey == 167))
    {
        CoreWindow window = Window.Current.CoreWindow;
        CoreVirtualKeyStates down = CoreVirtualKeyStates.Down;

        // Игнорировать комбинации с нажатыми клавишами Shift или Ctrl
        if ((window.GetKeyState(VirtualKey.Shift) & down) == down ||
            (window.GetKeyState(VirtualKey.Control) & down) == down)
        {
            return;
        }

        // Получение состояния клавиши Alt
        bool alt = (window.GetKeyState(VirtualKey.Menu) & down) == down;

        // Переход назад по комбинации Alt+← или виртуальной клавише браузера
        if (args.VirtualKey == VirtualKey.Left && alt ||
            (int)args.VirtualKey == 166 && !alt)
        {
            GoBack();
            args.Handled = true;
        }

        // Переход вперед по комбинации Alt+→ или виртуальной клавише браузера
        if (args.VirtualKey == VirtualKey.Right && alt ||
            (int)args.VirtualKey == 167 && !alt)
        {
            GoForward();
            args.Handled = true;
        }
    }
}

Клавиши <- и -> используются для перехода только в том случае, если одновременно с ними нажата клавиша Alt, но не нажаты ни Shift, ни Ctrl, а специальные браузерные клавиши принимаются только при отсутствии нажатых клавиш-модификаторов.

Метод GetKeyState немного неудобен тем, что он может возвращать один из трех членов перечисления CoreVirtualKeyStates: None (0), Down (1) или Locked (2). Во внутренней реализации все клавиши рассматриваются как переключатели с двумя состояниями, а члены перечисления - как флаги. Не нажатая клавиша находится в исходном состоянии 0. При нажатии она переходит в состояние 3, а при отпускании - в состояние 2. Повторное нажатие вызывает переход в состояние 1, а отпускание возвращает клавишу в состояние 0.

Пройди тесты
Лучший чат для C# программистов