UWP beginner: Заголовок окна в приложениях (VB.NET + C#)

в 8:11, , рубрики: C#, microsoft, uwp, vb.net, windows, XAML, Блог компании Microsoft, разработка под windows, универсальная платформа

С выходом Windows 10 компания Microsoft вернула понятие окна в его первозданном виде для приложений из Windows Store. Теперь, используя привычные кнопки в правом верхнем углу, можно менять размер, сворачивать/разворачивать и закрывать окно, независимо от типа приложения. Самое интересное, что Microsoft не отказалась от своего изначального направления – ориентации на контент. Эта философия не универсальна, но она хорошо подходит для целого пласта приложений. Согласитесь, если вы делаете игру или читалку, то стандартный заголовок с цветами заданными автоматически может слегка подпортить дизайн приложения. Поэтому новые широкие возможности настройки заголовка в приложениях универсальной платформы Windows (UWP) стали безусловным плюсом.

Данный материал подготовлен совместно с активным участником сообщества Microsoft Developer, Алексеем Плотниковым. В нем мы поговорим о возможных настройках заголовка внутри UWP-приложения.

UWP beginner: Заголовок окна в приложениях (VB.NET + C#) - 1

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

Сделать это крайне просто:

VB.NET

'Переход в полноэкранный режим
        ApplicationView.GetForCurrentView.TryEnterFullScreenMode()
'Возврат в оконный режим
        ApplicationView.GetForCurrentView.ExitFullScreenMode()

C#

//Переход в полноэкранный режим
     ApplicationView.GetForCurrentView().TryEnterFullScreenMode();
     
//Возврат в оконный режим
     ApplicationView.GetForCurrentView().ExitFullScreenMode();

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

Данный код лучше всего использовать в случае, когда необходимо перевести приложение в полноэкранный режим и обратно в ответ на действия пользователя. В случае, если требуется запуск приложения в полноэкранном режиме, нужно поместить в начале метода OnLaunched приложения следующий код:

VB.NET

ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen

C#

ApplicationView.PreferredLaunchWindowingMode = ApplicationViewWindowingMode.FullScreen;

Приятное дополнение такого подхода в том, что приложение сразу запустится в полноэкранном режиме, включая экран-заставку, а также запомнит данное правило для последующих запусков.

На заметку: для унификации поведения UWP-приложений и WinRT, при переходе в полноэкранный режим можно увидеть строку заголовка если подвести курсор мыши к верхнему краю окна. Это будет полезно при проектировании приложения.

2. Тонкая настройка. На самом деле, возможность полностью заменить стандартный заголовок на свой без необходимости заботится о стандартном поведении вроде перетаскивания окна за заголовок появилась впервые. По сути это значит, что теперь можно разместить на месте заголовка все, что придет в голову. Данный способ позволяет заменить только ту часть заголовка, которая содержит иконку и наименование приложения. Часть с кнопками управления окном заменить на собственную не получится.

Если погрузиться в этот функционал, слово «замена» не совсем подходит. Скорее мы просим платформу скрыть стандартный заголовок и говорим какой элемент в XAML будет выполнять его функции.

Делается всего двумя строчками кода:

VB.NET

'Разрешить использовать собственный заголовок (как следствие убрать стандартный)
        Core.CoreApplication.GetCurrentView.TitleBar.ExtendViewIntoTitleBar = True
'Установить указанный элемент как заголовок окна.
        Window.Current.SetTitleBar(CustomTitleBar)

С#

//Разрешить использовать собственный заголовок (как следствие убрать стандартный)
            CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
//Установить указанный элемент как заголовок окна.
            Window.Current.SetTitleBar(CustomTitleBar);

И вот здесь начинается самое интересное. Указанный элемент может не только сильно отличаться от привычного заголовка, но и располагаться в любом месте окна. Вторая строчка кода не поместит его в нужное место и не даст правильных размеров, она всего лишь уведомит платформу о том, что по мнению разработчика является заголовком. Единственное, что получится в итоге – это возможность перетаскивать окно нажатием на данный элемент, без необходимости дополнительных действий в коде.

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

Как это выглядит в XAML:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="32"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Background="LightBlue" x:Name="CustomTitleBur">
            <TextBlock VerticalAlignment="Center" Margin="10,0,0,0">Заголовок окна</TextBlock>
            <Image Margin="20,4,0,4" Source="Assets/StoreLogo.png"></Image>
        </StackPanel>
    </Grid>

В данном примере видно, что текст заголовка и иконка поменялись местами. Все выглядит предельно просто и понятно, но при этом есть целый ряд важных аспектов.

Во-первых, если сообщить платформе о том, что нужно использовать собственный заголовок, она отдаст ранее не доступную часть окна под макет, а значит все содержимое сдвинется вверх. Учитывая это, нужно позаботиться о том, чтобы в верхней части макета был только элемент заменяющий стандартный заголовок, а также убедиться, что ничто не перекрывается кнопками управления окном. В XAML выше корневой Grid отдает под заголовок одну строку высотой в 32 единицы. Здесь кроется еще один подвох – высота кнопок управления окном зависит от настроек Windows и равна 32 только при стандартных настройках, поэтому уважающий своего пользователя разработчик позаботится об отслеживании изменения настроек и отреагирует на это. В статье MSDN, посвященной методу Window.SetTitleBar можно узнать подробнее о том, как это сделать.

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

Вам может показаться, что обойти предыдущее ограничение можно просто убрав строчку кода:

VB.NET

Window.Current.SetTitleBar(CustomTitleBar)

C#

Window.Current.SetTitleBar(CustomTitleBar);

Но, платформа просто скроет стандартный заголовок, а необходимый элемент в качестве заголовка не установит. Тогда можно просто определить участки, за которые окно можно перетаскивать и добавить нужные действия в коде? Но, нет, все не так просто. На самом деле, если не вызвать метод SetTitleBar, то все что находится в макете окна попадет на место бывшего заголовка, потеряет возможность отлавливать ввод и будет вести себя как заголовок. Для чего же тогда нужна эта строчка кода? Она нужна для того, чтобы точно определить места, ведущие себя как заголовок. Если необходимо поместить туда кнопку, то она должна быть за пределами элемента CustomTitleBar.

Перепишем наш XAML следующим образом:

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="32"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Button HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="FullScreenButton"
              Click="FullScreenButton_Click">
                <FontIcon FontFamily="{ThemeResource SymbolThemeFontFamily}" Glyph=""/>
            </Button>
            <Grid Background="LightBlue" Grid.Column="1" x:Name="CustomTitleBar">
                <StackPanel Orientation="Horizontal">
                    <TextBlock VerticalAlignment="Center" Margin="10,0,0,0">Заголовок окна</TextBlock>
                    <Image Margin="20,4,0,4" Source="Assets/StoreLogo.png"></Image>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>

А в коде окна будет написано следующее:

VB.NET

    Private Sub Page_Loaded(sender As Object, e As RoutedEventArgs)
       Core.CoreApplication.GetCurrentView.TitleBar.ExtendViewIntoTitleBar = True
       Window.Current.SetTitleBar(CustomTitleBur)
        AddHandler Window.Current.SizeChanged, AddressOf CurrentWindow_SizeChanged
    End Sub

    Private Sub CurrentWindow_SizeChanged(sender As Object, e As WindowSizeChangedEventArgs)
        Dim view = ApplicationView.GetForCurrentView
        If Not view.IsFullScreenMode Then
            view.ExitFullScreenMode()
            CustomTitleBur.Visibility = Visibility.Visible
            FullScreenButton.Visibility = Visibility.Visible
        End If
    End Sub

    Private Sub FullScreenButton_Click(sender As Object, e As RoutedEventArgs)
        Dim view = ApplicationView.GetForCurrentView
        If view.TryEnterFullScreenMode() Then
            CustomTitleBur.Visibility = Visibility.Collapsed
            FullScreenButton.Visibility = Visibility.Collapsed
        End If
    End Sub

C#

    public MainPage()
        {
            this.InitializeComponent();
            this.Loaded += MainPage_Loaded;
            
        }

        private void MainPage_Loaded(object sender, RoutedEventArgs e)
        {
            Windows.ApplicationModel.Core.CoreApplication.GetCurrentView().TitleBar.ExtendViewIntoTitleBar = true;
            Window.Current.SetTitleBar(CustomTitleBar);
            Window.Current.SizeChanged += Current_SizeChanged;
            FullScreenButton.Click += FullScreenButton_Click;
            
        }

        private void FullScreenButton_Click(object sender, RoutedEventArgs e)
        {
            var view = ApplicationView.GetForCurrentView();

            if (!view.TryEnterFullScreenMode())
            {
                CustomTitleBar.Visibility = Visibility.Visible;
                FullScreenButton.Visibility = Visibility.Collapsed;
            }
            
        }

        private void Current_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
        {
            var view = ApplicationView.GetForCurrentView();

            if (!view.IsFullScreenMode)
            {
                view.ExitFullScreenMode();
                CustomTitleBar.Visibility = Visibility.Visible;
                FullScreenButton.Visibility = Visibility.Visible;
            }  
        }

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

Итак, рассмотрим пример более подробно. Для начала, чтобы получить возможность вызова события нажатия на кнопку, нужно внести ее за пределы элемента CustomTitleBar. Сам же CustomTitleBar – обернуть в закрашенный Grid, так как без заливки он не сможет отлавливать ввод мыши, и будет невозможно перетаскивать окно за весь заголовок. Кроме описанных ранее строчек, скрывающих стандартный заголовок и устанавливающих необходимый, в события загрузки окна добавляется подписка на событие изменения размеров окна.

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

Экспериментируя с данным кодом, можно добиться многих интересных результатов, но не стоит забывать о стандартизации и удобстве. Например, вы можете поместить CustomTitleBar внизу окна и тогда появится возможность перетаскивать окно не за верхнюю, а за нижнюю часть, но пользователь вряд ли скажет за это «спасибо». Хорошо обдумайте необходимость решения, прежде чем его применять. Кстати очень хорошим примером реализации собственного заголовка можно назвать браузер Microsoft Edge, в котором частью заголовка являются вкладки.
3. Смена цвета заголовка. Описанный выше метод крайне интересен и достаточно гибок, но в большинстве сценариев будет излишне создавать свой элемент и усложнять жизнь его правильной настройкой. Намного проще просто перекрасить элементы системного заголовка в цвета приложения. Набор параметров, отвечающих за цвета заголовка, достаточно широк, чтобы достичь гармонии с остальным приложением.

Для сокращения кода можно получить ссылку на текущий TitleBar и далее работать с ней:

VB.NET

Dim tb As ApplicationViewTitleBar = ApplicationView.GetForCurrentView.TitleBar

C#

ApplicationViewTitleBar tb = ApplicationView.GetForCurrentView().TitleBar;

Рассмотрим все свойства, отвечающие за цвета элементов заголовка:

VB.NET

'Устанавливает фона заголовка, но не затрагивает часть с кнопками управления окном.
tb.BackgroundColor = Windows.UI.Colors.LightBlue
'Устанавливает фон под кнопками управления окном
tb.ButtonBackgroundColor = Windows.UI.Colors.LightBlue
'Устанавливает цвет символов внутри кнопок управления окном (черточка, крестик, квадратик).
tb.ButtonForegroundColor = Windows.UI.Colors.Black
'Устанавливает цвет фона кнопок управления окном в момент наведения на них мыши. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
tb.ButtonHoverBackgroundColor = Windows.UI.Colors.Blue
'Устанавливает цвет символов внутри кнопок управления окном в момент наведения на них мыши. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
tb.ButtonHoverForegroundColor = Windows.UI.Colors.White
'Устанавливает цвет фона кнопок управления окном в момент, когда приложение не активно
tb.ButtonInactiveBackgroundColor = Windows.UI.Colors.Gray
'Устанавливает цвет символов внутри кнопок управления окном в момент, когда приложение не активно
tb.ButtonInactiveForegroundColor = Windows.UI.Colors.White
'Устанавливает цвет фона кнопок управления окном в момент нажатия на них. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
tb.ButtonPressedBackgroundColor = Windows.UI.Colors.DarkBlue
'Устанавливает цвет символов внутри кнопок управления окном в момент нажатия на них. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
tb.ButtonPressedForegroundColor = Windows.UI.Colors.White
'Устанавливает цвет символов заголовка окна
tb.ForegroundColor = Windows.UI.Colors.Black
'Устанавливает фона заголовка в момент, когда приложение не активно
tb.InactiveBackgroundColor = Windows.UI.Colors.Gray
'Устанавливает цвет символов заголовка окна в момент, когда приложение не активно
tb.InactiveForegroundColor = Windows.UI.Colors.White

C#

//Устанавливает фона заголовка, но не затрагивает часть с кнопками управления окном.
            tb.BackgroundColor = Windows.UI.Colors.LightBlue;
//Устанавливает фон под кнопками управления окном
            tb.ButtonBackgroundColor = Windows.UI.Colors.LightBlue;
//Устанавливает цвет символов внутри кнопок управления окном (черточка, крестик, квадратик).
            tb.ButtonForegroundColor = Windows.UI.Colors.Black;
//Устанавливает цвет фона кнопок управления окном в момент наведения на них мыши. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
            tb.ButtonHoverBackgroundColor = Windows.UI.Colors.Blue;
//Устанавливает цвет символов внутри кнопок управления окном в момент наведения на них мыши. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
            tb.ButtonHoverForegroundColor = Windows.UI.Colors.White;
//Устанавливает цвет фона кнопок управления окном в момент, когда приложение не активно
            tb.ButtonInactiveBackgroundColor = Windows.UI.Colors.Gray;
//Устанавливает цвет символов внутри кнопок управления окном в момент, когда приложение не активно
            tb.ButtonInactiveForegroundColor = Windows.UI.Colors.White;
//Устанавливает цвет фона кнопок управления окном в момент нажатия на них. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
            tb.ButtonPressedBackgroundColor = Windows.UI.Colors.DarkBlue;
//Устанавливает цвет символов внутри кнопок управления окном в момент нажатия на них. ВНИМАНИЕ! Не влияет на кнопку закрытия окна.
            tb.ButtonPressedForegroundColor = Windows.UI.Colors.White;
//Устанавливает цвет символов заголовка окна
            tb.ForegroundColor = Windows.UI.Colors.Black;
//Устанавливает фона заголовка в момент, когда приложение не активно
            tb.InactiveBackgroundColor = Windows.UI.Colors.Gray;
//Устанавливает цвет символов заголовка окна в момент, когда приложение не активно
            tb.InactiveForegroundColor = Windows.UI.Colors.White;

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

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

Стоит заметить, что настройки, отвечающие за цвета кнопок управления окном, также отвечают за кнопку «Назад», которая появилась в UWP-приложениях и расположена в заголовке окна (о ней планируем написать отдельный материал).

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

Автор: Microsoft

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js