Предыдущая лекция |   | Следующая лекция :----------------:|:----------:|:----------------: [Библиотеки классов](./t7_dll.md) | [Содержание](../readme.md#тема-8-оконные-приложения) | [Ресурсы](./wpf_resource.md) # Обзор типов оконных приложений в C#. Знакомство со структурой проекта WPF/Avalonia. Компоновка. Image. Ресурсы. >Содрано [отсюда](https://metanit.com/sharp/wpf/1.php) * [Технологии создания оконных приложений](#технологии-создания-оконных-приложений) * [Особенности платформ WPF/Avalonia](#особенности-платформ-wpfavalonia) * [Установка Avalonia](#установка-avalonia) * [Создание оконного приложения](#создание-оконного-приложения) * [Структура проекта](#структура-проекта) * [Компоновка](#компоновка) * [Image. Ресурсы](#image-ресурсы) ## Технологии создания оконных приложений В C# есть несколько технологий для созданий оконных приложений: * **Windows Forms** - разработка "классических" приложений Windows, считается устаревшей **Windows Forms** — интерфейс программирования приложений (API), отвечающий за графический интерфейс пользователя и являющийся частью *Microsoft .NET Framework*. Данный интерфейс упрощает доступ к элементам интерфейса Microsoft Windows за счет создания обёртки для существующего *Win32 API* в управляемом коде. Причём управляемый код — классы, реализующие API для **Windows Forms**, не зависят от языка разработки. * WPF (Window Presentation Foundation) - более современный фреймворк для .NET Framework, но заточен только под Windows (отрисовку реализует через **DirectX**) >Стоит отметить, что *.NET Framework* считается устаревшей технологией и на смену ей пришла кроссплатформенная библиотека *.NET Core*. Для неё есть аналог **WPF** - **Avalonia**, совместимый, на уровне разметки, фреймворк, работающий на **OpenGL**. * UWP (Universal Windows Platform) - вроде как "последний писк", рассчитанный на разработку универсальных приложений под Windows Phone, Windows 8 и.т.д **Avalonia** являеся практически калькой с **WPF**, поэтому далее я их рассматриваю одновременно. ## Особенности платформ WPF/Avalonia Если при создании традиционных приложений на основе **Windows Forms** за отрисовку элементов управления и графики отвечали такие части ОС Windows, как **User32** и **GDI+**, то приложения **WPF** основаны на **DirectX** (**Avalonia**, соответственно, на **OpenGL**). В этом состоит ключевая особенность рендеринга графики: используя **WPF/Avalonia**, значительная часть работы по отрисовке графики, как простейших кнопочек, так и сложных 3D-моделей, ложиться на графический процессор на видеокарте, что также позволяет воспользоваться аппаратным ускорением графики. Одной из важных особенностей является использование языка декларативной разметки интерфейса XAML, основанного на XML: вы можете создавать насыщенный графический интерфейс, используя или декларативное объявление интерфейса, или код C#, либо совмещать и то, и другое. ### Преимущества WPF/Avalonia Что вам, как разработчику, предлагает **WPF/Avalonia**? * Использование традиционных языков .NET-платформы - C# для создания логики приложения * Возможность декларативного определения графического интерфейса с помощью специального языка разметки XAML, основанном на XML и представляющем альтернативу программному созданию графики и элементов управления, а также возможность комбинировать XAML и C# * Независимость от разрешения экрана: поскольку в WPF/Avalonia все элементы измеряются в независимых от устройства единицах, приложения на WPF/Avalonia легко масштабируются под разные экраны с разным разрешением. * Новые возможности, которых сложно было достичь в **Windows Forms**, например, создание трехмерных моделей, привязка данных, использование таких элементов, как стили, шаблоны, темы и др. * Хорошее взаимодействие с **Windows Forms** (только **WPF**), благодаря чему, например, в приложениях **WPF** можно использовать традиционные элементы управления из **Windows Forms**. * Богатые возможности по созданию различных приложений: это и мультимедиа, и двухмерная и трехмерная графика, и богатый набор встроенных элементов управления, а также возможность самим создавать новые элементы, создание анимаций, привязка данных, стили, шаблоны, темы и многое другое * Аппаратное ускорение графики - вне зависимости от того, работаете ли вы с 2D или 3D, графикой или текстом, все компоненты приложения транслируются в объекты, понятные Direct3D/OpenGL, и затем визуализируются с помощью процессора на видеокарте, что повышает производительность, делает графику более плавной. ## Установка Avalonia Далее мы будем использовать Avalonia, но так как он не входит в .NET, то нужно его установить: 1. В основном окне **Rider** выбрать вкладку *Configure -> Plugins*: ![](../img/rider12.png) И установите `AvaloniaRider` (в строке поиска введите "avalonia") При установке может выдать сообщение, что плагин разработан не в **JetBrains** и использовать на свой страх - соглашаемся (**Accept**) 1. После установки плагина перезагрузите IDE и установите шаблоны проектов для **Avalonia**. В консоли выполните команду: ``` dotnet new install Avalonia.Templates ``` >Для .NET 6.0 и более ранних версий замените install на --install. >Версию .NET можно узнать выполнив в консоли команду `dotnet --version` ## Создание оконного приложения Запустите **Rider** и создайте новое решение: Если шаблоны установлены нормально, то в окне создания нового проекта появится секция *Other*: Выберите **Avalonia .NET Core App** При создании задаете *Название решения*, *Имя проекта* и, если нужно, *Расположение*. Остальные параметры оставляем по-умолчанию. >Название проекта должно отражать предметную область или название компании (за это есть отдельные баллы на чемпионате и демо-экзамене) **Основные типы файлов проекта:** * **.AXAML** (Avalonia eXtended Application Markup Languale) - язык разметки, очень похож на XML. В таких файлах хранится описание внешнего вида окна. * **.cs** - файлы с исходным кодом на C# для окна. ## Структура проекта В структуре проекта следует выделить следующие моменты: **Во-первых**, в проекте имеется файл `App.axaml` и связанный с ним файл кода `App.axaml.cs` - это глобальные файлы для всего приложения, позже мы о них поговорим подробнее. А пока только следует знать, что в `App.axaml.cs` задается класс главного окна программы, которое будет открываться при запуске приложения. Если вы откроете этот файл, то можете найти в нем строку `desktop.MainWindow = new MainWindow();` - то есть в данном случае, когда мы запустим приложение, главным будет окно из класса `MainWindow`. Далее в структуре определены файл разметки `MainWindow.axaml` и файл связанного кода `MainWindow.axaml.cs`. Файл `MainWindow.axaml` и представляет определение окна приложения, которое мы увидим при запуске. ### Введение в язык XAML **AXAML (Avalonia eXtensible Application Markup Language)** - язык разметки, используемый для инициализации объектов в технологиях на платформе .NET. Применительно к **Avalonia** данный язык используется прежде всего для создания пользовательского интерфейса декларативным путем, наподобие HTML в веб-программировании. **AXAML** - не является обязательной частью приложения, мы вобще можем обходиться без него, создавая все элементы в файле связанного с ним кода на языке C#. Однако использование AXAML все-таки несет некоторые преимущества: * Возможность отделить графический интерфейс от логики приложения, благодаря чему над разными частями приложения могут относительно автономно работать разные специалисты: над интерфейсом - дизайнеры, над кодом логики - программисты. * Компактность, понятность, код на AXAML относительно легко поддерживать. При компиляции приложения код в axaml-файлах также компилируется в бинарное представление кода axaml. И затем это бинарное представление встраивается в финальную сборку приложения - exe или dll-файл. #### Структура и пространства имен AXAML При создании нового проекта он уже содержит файлы с кодом axaml. Так, создаваемый по умолчанию в проекте файл `MainWindow.axaml` будет иметь следующую разметку: ```xml Welcome to Avalonia! ``` Если вы совершенно не знакомы с axaml и с xml, то даже этот небольшой минимальный код окна может вызывать затруднения. Подобно структуре веб-страничке на html, здесь есть некоторая иерархия элементов. Элементом верхнего уровня является тег **Window**, который представляет собой окно приложения. При создании других окон в приложении нам придется всегда начинать объявление интерфейса с элемента **Window**, поскольку это элемент самого верхнего уровня. Кроме **Window** существует еще два элемента верхнего уровня: * Page * Application Элемент **Window** имеет вложенный текст (Welcome to Avalonia!), а также подобно html-элементам ряд атрибутов (*Title*, *DesignWidth*, *DesignHeight*) - они задают заголовок, ширину и высоту окна соответственно. #### Пространства имен AXAML При создании кода на языке C#, чтобы нам были доступны определенные классы, мы подключаем пространства имен с помощью директивы using, например, `using Avalonia.Controls;`. Чтобы задействовать элементы в AXAML, мы также подключаем пространства имен. Аттрибуты **xmlns** как раз и представляют собой пространства имен, подключаемые в проект. Так, пространство имен **https://github.com/avaloniaui** содержит описание и определение большинства элементов управления. Так как является пространством имен по умолчанию, то объявляется без всяких префиксов. **http://schemas.microsoft.com/winfx/2006/xaml** - это пространство имен, которое определяет некоторые свойства AXAML, например свойство _Name_ или _Key_. Используемый префикс `x` в определении `xmlns:x` означает, что те свойства элементов, которые заключены в этом пространстве имен, будут использоваться с префиксом x - `x:Name` или `x:Key`. Это же пространство имен используется уже в аттрибуте `x:Class="AvaloniaFirst.MainWindow"` - здесь создается ссылка на класс **MainWindow** и соответствующий ему файл кода, куда будет прописываться логика для данного окна приложения. Это два основных пространства имен. Рассмотрим остальные: **xmlns:d="http://schemas.microsoft.com/expression/blend/2008"**: предоставляет поддержку атрибутов в режиме дизайнера. Это пространство имен преимущественно предназначено для другого инструмента по созданию дизайна на XAML - Microsoft Expression Blend **xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"**: обеспечивает режим совместимости разметок XAML. Важно понимать, что эти пространства имен не эквивалентны тем пространствам имен, которые подключаются при помощи директивы **using** в c#. #### Элементы и их атрибуты XAML предлагает очень простую и ясную схему определения различных элементов и их свойств. Каждый элемент, как и любой элемент XML, должен иметь открывающий и закрывающий теги, как в случае с элементом Window: ```xml ``` Либо элемент может иметь сокращенню форму с закрывающим слешем в конце, наподобие: ```xml ``` Но в отличие от элементов xml каждый элемент в XAML соответствует определенному классу C#. Например, элемент **Button** соответствует классу **Avalonia.Controls.Button**. А свойства этого класса соответствуют атрибутам элемента **Button**. Например, добавим кнопку в создаваемую по умолчанию разметку окна: ```xml ``` Здесь свойство *Content* задается неявно в виде содержимого между тегами ``. Но несмотря на то, что у нас несколько пробелов между словами "Hello" и "World", XAML по умолчанию будет убирать все эти пробелы. И чтобы сохранить пробелы, нам надо использовать атрибут `xml:space="preserve"`: ```xml ``` ### Файлы отделённого кода При создании нового проекта в дополнение к создаваемому файлу `MainWindow.axaml` создается также файл отделённого кода `MainWindow.axaml.cs`, где, как предполагается, должна находится логика приложения связанная с разметкой из `MainWindow.axaml`. Файлы XAML позволяют нам определить интерфейс окна, но для создания логики приложения, например, для определения обработчиков событий элементов управления, нам все равно придется воспользоваться кодом C#. По умолчанию в разметке окна используется атрибут `x:Class`: ```xml Здесь проявляется основное отличие **Windows Forms** и **WPF/Avalonia**: в **Windows Forms** код первичен, т.е. при старте приложения **императивно** создаётся интерфейс (вызовами Windows API), а в **WPF** первичен интерфейс, в котором **декларативно** описан внешний вид и при запуске приложения запускается класс окна, который мы можем расширить (инициализировать источники данных и описать логику работы). #### Взаимодействие кода C# и XAML В приложении часто требуется обратиться к какому-нибудь элементу управления. Для этого надо установить у элемента в XAML свойство **Name**. Еще одной точкой взаимодействия между xaml и C# являются события. С помощью атрибутов в XAML мы можем задать события, которые будут связанны с обработчиками в коде C#. Итак, в разметке главного окна определим два элемента: кнопку и текстовое поле. >В теге **Window** может быть только один вложенный элемент. Обычно используют **Grid** (подробнее о нём поговорим ниже) ```xml ``` Так как пространство имен проекта проецируется на префикс `local`, то все классы проекта можно использовать в форме `local:Название_Класса`. Так в данном случае в качестве содержимого кнопки устанавливается объект **Phone**. ![](../img/rider14.png) Для вывода содержимого любого объекта используется метод *ToString*. Этот метод объявлен в классе **Object** и по-умолчанию выводит просто название класса, в нашем случае **AvaloniaFirst.Phone**. Для того, чтобы получить нужную нам информацию об объекте необходимо переопределить метод *ToString*. Мы можем подключить любые другие пространства имен, классы которых мы хотим использовать в приложении. Например: ```cs Понедельник Вторник Среда Четверг Пятница Суббота Воскресенье ``` Здесь определены два дополнительных пространства имен: ``` xmlns:col="clr-namespace:System.Collections;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=mscorlib" ``` Благодаря этому нам становятся доступными объекты из пространств имен **System.Collections** и **System**. И затем используя префикс, мы можем использовать объекты, входящие в данные пространства имен: ` ``` Каждая строка задается с помощью вложенного элемента *RowDefinition*. При этом задавать дополнительную информацию необязательно. То есть в данном случае у нас определено в гриде 3 строки. Каждая столбец задается с помощью вложенного элемента *ColumnDefinition*. Таким образом, здесь мы определили 3 столбца. То есть в итоге у нас получится таблица 3х3. Для визуального отображения ячеек сетки можно добавить свойство `ShowGridLines="True"` ![](../img/rider15.png) Чтобы задать позицию элемента управления с привязкой к определенной ячейке **Grid**-а, в разметке элемента нужно прописать значения свойств *Grid.Column* и *Grid.Row*, тем самым указывая, в каком столбце и строке будет находиться элемент. Кроме того, если мы хотим растянуть элемент управления на несколько строк или столбцов, то можно указать свойства *Grid.ColumnSpan* и *Grid.RowSpan*, как в следующем примере: ```xml