Контекстные меню в WinRT

190

Собрав воедино элементы, элементы управления и панели, о которых в предыдущих статьях, можно сконструировать весь пользовательский интерфейс на поверхности страницы. Однако во многих программах желательно, чтобы большинство команд и функций программы оставались скрытыми до тех пор, пока пользователь не захочет воспользоваться ими.

В традиционных приложениях Windows для консолидации команд и функций обычно использовались меню и диалоговые окна. Хотя верхний уровень меню всегда оставался видимым, сами команды обычно размещались в раскрывающихся подменю. Некоторые команды меню открывали диалоговые окна с группами логически связанных функциональных возможностей.

В Windows 8 основное внимание уделяется содержанию, а не «внешней отделке» приложений. Во многих случаях функции, ранее находившиеся в меню приложения, были перемещены на строку приложения, которая обычно скрыта, но активизируется, когда пользователь проводит пальцем у верхнего или нижнего края экрана или подводит указатель мыши к этим областям. Строка приложения представлена производным от ContentControl классом с именем AppBar.

Кроме того, приложение Windows 8 может вывести список команд в простом объекте типа PopupMenu (часто используемом как контекстное меню) или в более обширном наборе элементов управления, для представления которого используется элемент с именем Popup. Позже я продемонстрирую использование обоих типов всплывающих окон.

Реализация контекстных меню

Контекстное меню вызывается правой кнопкой мыши или жестом «нажать-удерживать-отпустить». Меню появляется в точке прикосновения к экрану и обычно исчезает при выборе одной из команд. Очень часто контекстное меню связывается с конкретным элементом управления или конкретной областью одного элемента; собственно, поэтому оно и называется «контекстным».

Элемент управления TextBox включает контекстное меню. Чтобы увидеть его, запустите любую программу, в которой используется TextBox. Введите текст в поле, выделите фрагмент и щелкните правой кнопкой мыши на элементе управления (или выполните жест «нажать-удерживать-отпустить»). На экране появляется меню, которое может содержать до пяти команд в зависимости от выделения и состояние буфера обмена:

Контекстное меню для элемента TextBox

Чтобы создать собственное контекстное меню, создайте объект типа PopupMenu. Этот класс определяется в пространстве имен Windows.UI.Popups вместе с классом MessageDialog и UICommand, используемым для задания команд в MessageDialog и PopupMenu.

PopupMenu является производным от Object, поэтому крайне маловероятно, чтобы его экземпляр создавался в визуальном дереве в файле XAML. Почти наверняка объект PopupMenu будет конструироваться полностью в программном коде в момент вызова, скорее всего, по событию RightTapped.

В следующем файле XAML содержится элемент TextBlock, выровненный по центру страницы с обработчиком, назначенным событию RightTapped:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Name="textBlock"
                   FontSize="24"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   TextAlignment="Center"
                   RightTapped="OnTextBlockRightTapped">
            Простое контекстное меню
            <LineBreak />
            <LineBreak />
            (щелкните правой кнопкой мыши или нажмите и удерживайте палец на сенсорном экране)
        </TextBlock>
</Grid>

Как и в случае с MessageDialog, содержащиеся в меню команды задаются экземплярами UICommand. Меню отображается вызовом ShowAsync():

using System;
using Windows.UI;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;

namespace WinRTTestApp
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private async void OnTextBlockRightTapped(object sender, RightTappedRoutedEventArgs args)
        {
            PopupMenu popupMenu = new PopupMenu();
            popupMenu.Commands.Add(new UICommand("Крупный шрифт", OnFontSizeChanged, 1.2));
            popupMenu.Commands.Add(new UICommand("Мелкий шрифт", OnFontSizeChanged, 1 / 1.2));
            popupMenu.Commands.Add(new UICommandSeparator());
            popupMenu.Commands.Add(new UICommand("Красный", OnColorChanged, Colors.Red));
            popupMenu.Commands.Add(new UICommand("Зеленый", OnColorChanged, Colors.Green));
            popupMenu.Commands.Add(new UICommand("Синий", OnColorChanged, Colors.Blue));

            await popupMenu.ShowAsync(args.GetPosition(this));
        }

        private void OnFontSizeChanged(IUICommand command)
        {
            textBlock.FontSize *= (double)command.Id;
        }

        private void OnColorChanged(IUICommand command)
        {
            textBlock.Foreground = new SolidColorBrush((Color)command.Id);
        }
    }
}

Обратите внимание на использование объекта UICommandSeparator для создания горизонтальной разделительной линии в меню.

Как и в случае с MessageDialog, вызов ShowAsync() возвращает объект типа IAsyncOperation<IUICommand>, от которого можно получить команду, выбранную пользователем. Я вместо этого задал два пользовательских обработчика команд в конструкторах UICommand, а в третьем аргументе конструктора задал значение, позволяющее обработать команду с минимальными хлопотами.

Методу ShowAsync() необходимо значение Point, указывающее, в какой точке экрана должно отображаться меню. Точка задается относительно окна приложения, это обычно означает, что она задается относительно страницы. Центр меню по горизонтали выравнивается по этой точке, а само меню вертикально располагается над точкой. Такое расположение вполне логично для интерфейса сенсорного экрана: ведь вы не хотите, чтобы меню оказалось закрыто рукой пользователя!

Вот как выглядит меню:

Конечно, если щелкнуть за пределами TextBlock, не произойдет ничего. Если заданная точка расположена слишком близко к левому, верхнему или правому краю окна, меню автоматически сдвигается так, чтобы оно не выходило за край экрана. Меню всегда отображается черным текстом на белом фоне независимо от значения RequestedTheme.

Меню поддерживает клавиатурный интерфейс, но он весьма ограничен: клавиши со стрелками изменяют текущую команду меню, а клавиша Enter выбирает ее. Меню исчезает при выборе команды, при щелчке или касании за пределами меню или при нажатии любой другой клавиши. Если меню было закрыто без выбора команды, то при обработке объекта IUICommand, возвращаемого методом ShowAsync(), этот объект будет равен null.

Собственно, мы рассмотрели практически все, что можно сделать с PopupMenu. Осталось упомянуть только об альтернативном методе вызова меню ShowForSelectionAsync(). Этому методу передается значение Rect и необязательное значение из перечисления Placement, состоящего из членов Default, Above, Below, Left и Right. Последнее задает лишь желательное местонахождение меню: фактическая позиция выбирается так, чтобы все меню поместилось в окне программы.

Вывести меню PopupMenu с недоступными командами (выделенными серым шрифтом) невозможно. Если какая-либо команда неприменима в текущем контексте, не включайте ее!

Также невозможно отображение команд с «галочками», обозначающими выбранную команду. Если потребуется вывести что-то, кроме простых команд, придется перейти с PopupMenu на Popup.

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